Run an RTE
The settings of each mode, and how to start an RTE
Settings
What an RTE needs depends on its mode (see Modes). Every RTE reads these:
| Variable | Meaning |
|---|---|
RTE_MODE | Since 7.3.0: outbound (it dials the app), inbound (the app dials it) or database (deprecated, the default throughout 7.x) |
RTE_TOKEN | The token between the app and the RTE, at least 16 characters; the RTE refuses to start without it |
RTE_MAX_JOBS | How many playbooks it runs at once, 10 by default (0 sets no limit). A job for a full RTE waits until a playbook ends |
RTE_DRAIN_SECONDS | On a stop, how long it waits for its running playbooks, 25 by default; the ones still running then end as abandoned |
RTE_QUEUE_MINUTES | Read by the app nodes and the worker, not the RTE: how long a job waits for a full or stopping RTE, 60 by default |
ANSIBLE_PATH, PROCESS_MAX_BUFFER | The playbooks folder and the output limit: read by the RTE, so set them in its environment |
LISTEN_HOST | Since 7.3.0: the address its listeners bind, every interface by default; 127.0.0.1 when nothing dials it from outside (an outbound RTE) |
An RTE in a socket mode, outbound or inbound, also reads these, and no DB_* and no ENCRYPTION_SECRET:
| Variable | Meaning |
|---|---|
APP_URL | The app’s address as people open it, such as https://forms.example.com. Outbound: the address it dials. Inbound: the address of its one registration call |
APP_TLS_VERIFY | 1 (default): the app’s certificate must be valid. 0 accepts any, inside one trusted network only |
RTE_NAME | Its runner’s name, which forms name with runner:. Not set: its node id, rte-<hostname>-<port>, which changes with the container: set it in production |
RTE_JOIN_TOKEN | Optional, the first time only: a join token, made through the API, which approves its runner at once instead of waiting for an admin (see Join tokens) |
RTE_URL | Inbound, when it asks to join: the address the app dials, such as http://rte-dmz.example.com:8000 |
RTE_FILES | transfer (default): the app sends the playbooks and inventories with the job. local: it runs from its own mounted folders |
RTE_SPOOL_PATH | Where each job’s output, events and end wait until the app stored them, <persistent>/rte-spool by default: keep it on a volume |
RTE_CACHE_PATH | With transfer: where the folders the app sent are kept, <persistent>/rte-files by default |
RTE_CACHE_MB | With transfer: how much those folders may take, 1024 by default; the ones used longest ago go first, and 0 keeps none |
RTE_FILES_MAX_MB | Read by the app, not the RTE: the largest folder it sends with transfer, 512 by default |
On an RTE in a socket mode, RTE_MAX_JOBS, RTE_DRAIN_SECONDS, RTE_FILES and RTE_CACHE_MB can also be set on its runner page, under the RTE tab: a value there wins over the variable, which then only fills a field left empty (see An RTE’s settings). A value out of range is brought to its limit, and the RTE says so when it starts.
An RTE in database mode (deprecated) reads the database settings instead, and registers itself:
| Variable | Meaning |
|---|---|
DB_HOST, DB_PORT, DB_USER, DB_PASSWORD | The app’s own values: the RTE reads its jobs from the database and writes their output there |
RTE_URL | The address the app calls the RTE on, such as http://rte:8000. The runner is named after its host (rte), so forms can use runner: rte |
RTE_REGISTER | Deprecated in 7.3.0, with database mode: a socket-mode RTE joins with a join token instead. 1 (default): the RTE adds itself under Connections > Runners at its start, and the first one becomes the default runner. 0: add it there yourself, or in the config seed |
ENCRYPTION_SECRET | The app’s own value, needed only to register itself (the runner’s token is stored encrypted); leave it out with RTE_REGISTER=0 |
In database mode, without RTE_URL the RTE registers its IP address and port under the name rte-<hostname>-<port>, which change when the container is recreated: set RTE_URL in production. The worker removes a runner the RTE registered itself 10 minutes after the RTE’s last heartbeat.
Start it
The RTE runs the playbooks, so it needs its ssh folder, with the private key and the known_hosts the playbooks use, and a persistent folder for its spool and the files the app sends. Pick the mode:
Start the RTE on any host that reaches the app, with its own token and the name its runner gets:
docker run -d --name rte-dmz --restart unless-stopped --stop-timeout 40 \
-e RTE_MODE=outbound -e APP_URL=https://forms.example.com \
-e RTE_NAME=rte-dmz -e RTE_TOKEN=<at least 16 characters> -e HTTPS=0 \
-v /srv/apps/rte/persistent:/app/dist/persistent \
-v /srv/apps/rte/ssh:/home/node/.ssh \
ghcr.io/ansibleforms/ansibleforms-rte-full:7
It dials the app and asks to join: Connections > Runners shows the runner rte-dmz as waiting for approval, and its log shows the fingerprint of its token. Check that its review shows the same fingerprint, then Approve it (see Approving an RTE): it takes jobs from then on, signing in with its own RTE_TOKEN. For an unattended start, add -e RTE_JOIN_TOKEN=<a join token, afj_...>, which approves it at once. Nothing needs to reach this host: publish no port. Only /live, /ready and /metrics listen on its PORT, for the probes.
Start the RTE where the app reaches it. This one runs next to the app and runs the playbooks from its folders (RTE_FILES=local):
docker run -d --name rte --network ansibleforms --restart unless-stopped --stop-timeout 40 \
-e RTE_MODE=inbound -e RTE_TOKEN=<at least 16 characters> \
-e RTE_FILES=local -e PORT=8000 -e HTTPS=0 \
-v /srv/apps/ansibleforms/persistent:/app/dist/persistent \
-v /srv/apps/ansibleforms/ssh:/home/node/.ssh \
ghcr.io/ansibleforms/ansibleforms-rte-full:7
Then give the app its runner: in the config seed, an RTE credential and an inbound runner that signs in with it:
credentials:
items:
- name: rte
credential_type: rte
description: The RTE_TOKEN of the rte container, and its address
url: http://rte:8000 # where the app dials it
password: ${RTE_TOKEN} # its RTE_TOKEN, from the app's environment
runners:
items:
- name: rte
type: rte
rte_mode: inbound
credential: rte
is_default: true # playbook forms that name no runner run here
Or add the same two by hand: the credential under Connections > Credentials, type RTE, and the runner under Connections > Runners, type RTE, mode Inbound, with that credential. To let the RTE ask to join instead, start it with APP_URL and RTE_URL, and approve it under Connections > Runners (see Inbound).
Deprecated since 7.3.0 and removed in 8.0.0. The RTE runs the playbooks from its own disk, so mount the app’s persistent volume (playbooks, repositories, uploads) and its ssh folder on it too. A Docker example, with the app reachable as ansibleforms on the same network:
docker run -d --name rte --network ansibleforms \
-e DB_HOST=192.168.0.1 -e DB_PORT=3306 -e DB_USER=ansibleforms -e DB_PASSWORD=<password> \
-e ENCRYPTION_SECRET=<the app's> -e RTE_TOKEN=<at least 16 characters> \
-e RTE_URL=http://rte:8000 -e HTTPS=0 \
-v /srv/apps/ansibleforms/persistent:/app/dist/persistent \
-v /srv/apps/ansibleforms/ssh:/home/node/.ssh \
ghcr.io/ansibleforms/ansibleforms-rte-full:7
Keep an inbound RTE off the public network: its socket answers any caller that has the token. docker stop waits only 10 seconds before it kills a container, so give the RTE a longer stop timeout (--stop-timeout, or stop_grace_period in compose) or lower RTE_DRAIN_SECONDS. Kubernetes waits 30 seconds by default.
The installer sets up an outbound or inbound RTE on a server of its own, with Docker, Podman or without containers, and prints the fingerprint to approve it by. To run an RTE in another datacenter by hand, follow Another datacenter.