CLI & scripts
Taranac is administered almost entirely from its web UI. The command line exists
for the jobs the UI cannot do for itself: bringing the stack up, recovering an
admin who is locked out of the UI, building and operating an HA cluster,
collecting diagnostics, rotating the master key, updating to a new release, and
disaster recovery. All of it runs from the deployment bundle directory on the
host (the folder created when you unpacked the release and ran ./install.sh),
through one thin wrapper named taranac.
You do not need to know Docker to operate Taranac, but it helps to know that the
wrapper is just a shortcut around docker compose: anything you can do with
docker compose you can do by typing it after ./taranac.
- $ ./taranac unlock admin
- $ ./taranac update
- $ ./taranac ps
./taranac wrapper routes its convenience subcommands to its own handlers and passes everything else straight through to docker compose.The taranac wrapper
Section titled “The taranac wrapper”./taranac has two kinds of commands:
- Convenience subcommands it interprets itself —
unlock,reset-password,create-admin,installation-id,cluster,diagnose,restore,rotate-key,reset,version,update,captive-portal. - Everything else is passed straight through to
docker compose, so./taranac ps,./taranac logs,./taranac up -d,./taranac down,./taranac restart <service>all behave exactly like the compose commands of the same name.
./taranac ps # service status./taranac logs -f api # follow one service's logs./taranac restart edge # restart the edge (e.g. to apply a new TLS cert)./taranac up -d # start the stack (detached)./taranac down # stop everything (data in volumes is preserved)./taranac logs <service> for a service that has no container on this host says so
instead of printing nothing — no <service> container on this host., followed by the
containers this stack does have here, or ./taranac up -d when it has none — and
exits 1.
Where each subcommand actually runs
Section titled “Where each subcommand actually runs”Most of them are a docker compose exec into the api container. Several are
not: recovery, key rotation, updates, diagnostics and cluster building have to
rewrite host files, capture packets on the host’s interfaces or recreate
containers — none of which a container can do for itself — so they live in their
own scripts next to the wrapper.
| Subcommand | Where it runs |
|---|---|
unlock, reset-password, create-admin, installation-id, reset | inside the api container |
cluster status, verify, connectivity, switchover, join-token, register, revoke, decommission, manifest-* | inside the api container (app.scripts.cluster) |
cluster init, add-witness, add-node, remove-witness, sync | cluster-wizard.sh on the host |
cluster renew-cert, rotate-ca | the host’s etcd-agent container — on any member, the witness included |
cluster remove-node, cluster detach | cluster-remove.sh on the host |
diagnose | diagnose.sh — --ha re-execs diagnose-ha.sh |
captive-portal join, update, reset-trust, uninstall, status | portal-join.sh, against the portal installed beside the wrapper |
restore | taranac-restore.sh |
rotate-key | rotate-key.sh |
version, update | taranac-update.sh |
If one of those scripts is missing from the bundle the wrapper says so and tells
you to run ./taranac update once — it never falls through to something else.
One bundle, three kinds of host
Section titled “One bundle, three kinds of host”The same bundle is unpacked on a full Taranac node, on an HA witness and on a
standalone collector — that is how those hosts get their own join
scripts, and it puts this wrapper on all three. The wrapper resolves
which kind of host it is on first and drives that host’s compose file, so
./taranac ps on a witness shows the arbiter’s etcd rather than a product stack
that was never meant to run there.
On a witness or a collector, the commands that need the database or the API —
cluster …, unlock, reset-password, create-admin, installation-id,
reset — are refused with the reason and a list of what can be done on that
host. The exceptions on a witness are cluster renew-cert and cluster rotate-ca,
which run in its own etcd-agent. ./taranac diagnose --ha is deliberately allowed on the witness: an HA
fault is usually a disagreement between nodes, and support needs the arbiter’s
side of it. On a witness, down / stop / kill also print what stopping the
arbiter costs you in votes.
Bringing a host online
Section titled “Bringing a host online”These three scripts run once per host, in order, and are the only thing you run before the platform is up. After that, day-to-day work is the wrapper above.
| Script | Run as | What it does |
|---|---|---|
sudo bash bootstrap.sh | root | Installs Docker Engine + the compose plugin and adds your user to the docker group. Idempotent — skips anything already present, except a Docker older than 28.0 or Podman, which it refuses with the command that updates or replaces it. |
./install.sh | your user | First run: asks for your domain and admin details, generates every secret, writes .env, pulls images, starts the stack, and prints the admin login. |
./taranac restart edge | your user | Applies a trusted TLS certificate after you drop tls.crt + tls.key into config/tls/. |
install.sh takes exactly three options: --no-start, which writes the
configuration but does not pull or start anything (used to prepare a node that
will join an HA cluster, since a joining node must have an empty data volume);
--ipv6-own-address-ok, which lets a re-run on an existing installation go on although
inventory entries that matched IPv6 devices through the Docker gateway stop matching them
(IPv6); and -h /
--help. Anything else is refused by name. Before it writes anything it checks the host —
Docker 28.0 or newer, not Podman, IPv6 in the kernel, a dual-stack test network — and
refuses with the fix; see IPv6 → What the host needs.
On a standalone server, re-running install.sh when .env already exists
refreshes the images of the bundle’s version: it never regenerates secrets — it
backfills any a newer bundle introduced, repairs file permissions, runs the
inventory checks ./taranac update runs, pulls and restarts — recreating the stack’s
Docker network when its address plan changed. On an HA cluster node or a witness it refuses, changes nothing and names
./taranac update. It also refuses to start a database it finds on the host
without the .env that belongs to it, and a former cluster member’s database as a
standalone one. A release upgrade (images and bundle files) is always
./taranac update, described below.
Satellite hosts have their own join script
Section titled “Satellite hosts have their own join script”A host that is not a full Taranac node — an HA witness, a standalone
collector, a standalone captive portal — is brought online by one command that
enrolls it against the core. Each of them takes -h / --help.
| Script | Enroll with | Also takes |
|---|---|---|
witness-join.sh | --broker <host>:9443 --token <secret> --pin sha256:<fp> (tokens are hex; --token=<secret> works too) | --env-file <path> (default ./.env) · --print-auth (print the derived auth header and exit — a diagnostic for “invalid credentials”) · --leave (retire this witness after cluster remove-witness: refused while the host is still in the ring; stops the witness etcd, deletes its state and certificate, keeps .env as .env.left-<time>; --confirm NOT-A-MEMBER when no database node answers) |
collector-join.sh | --core <https url>, and the enrollment token at the prompt (it does not echo), from --enrollment-token-file <path|-> (- reads stdin) or from TARANAC_ENROLLMENT_TOKEN. --enrollment-token is refused | --ca-file <path> | --trust-any · --collector-id <label> · --poll-interval <sec> · --with-nmap (build nmap into a local image from your own mirrors, for the scanner’s Deep method — see Scanner) · --status · --reset-trust · --update (with --check, --version <X> or --from <image>.tar) · --uninstall (with --force to discard a non-empty outbox) |
portal-join.sh (usually run as ./taranac captive-portal join) | --core <https url>, and the portal token at the prompt, from --token-file <path|-> or from TARANAC_PORTAL_TOKEN. --token is refused | --ca-file <path> | --trust-any · --dir <dir> · --client-interface <if> · --uplink-interface <if> · --dhcp-range <first last> · --upstream-dns <ip> · --nat on|off · --client-isolation on|off · -y · --status · --update (with --check) · --reset-trust · --uninstall. See Standalone captive portal |
A collector or portal token is not taken as an argument, where it would sit in the
shell history and in ps.
A witness pins the join channel by a fingerprint the primary prints on its
console — not trust-on-first-use — and the normal way to get all three arguments
is to let ./taranac cluster add-witness print the command for you. A collector
is moved between versions with ./collector-join.sh --update, never with
./taranac update: a collector has to match the version its core runs, which
is not the newest release while you upgrade hosts one at a time.
A standalone captive portal is addressed by name
Section titled “A standalone captive portal is addressed by name”On a host that carries a standalone captive portal, the wrapper addresses it as
captive-portal, wherever its directory is (./captive-portal beside the wrapper
by default, or TARANAC_PORTAL_DIR):
| Command | What it does |
|---|---|
./taranac captive-portal join --core <url> [--trust-any] | Install or re-install the portal on this host; it asks for the portal token (or reads --token-file <path|-> / TARANAC_PORTAL_TOKEN). The core prints this line ready to paste on the portal’s Deployment tab |
./taranac captive-portal status | Trust, token, heartbeat and datapath, from local state only |
./taranac captive-portal datapath | The rules, the allowed clients and the speed limits in force |
./taranac captive-portal restart portal | Check the certificate in certs/ first, then restart; refuses — leaving the running portal untouched — if the certificate cannot be served |
./taranac captive-portal update [--check] | Move the portal to the version its core runs |
./taranac captive-portal reset-trust | Accept the core’s certificate again after a real replacement |
./taranac captive-portal uninstall | Remove the portal, and its datapath, from this host |
Anything else — logs -f portal, ps — is passed to docker compose in the portal’s
directory. See Standalone captive portal.
Admin recovery from the console
Section titled “Admin recovery from the console”The reason an operator reaches for the CLI most often is being locked out of the
web UI. Taranac locks an account after a number of failed logins
(MAX_LOGIN_ATTEMPTS, 5 by default) and the lock does not auto-expire — common
on an internet-facing host once someone starts guessing passwords. Since the lock
blocks the UI, recovery has to happen from the server console.
Each of these runs the corresponding tool inside the api container.
Unlock a locked account
Section titled “Unlock a locked account”./taranac unlock # list currently locked accounts./taranac unlock admin # unlock one account by username./taranac unlock --all # unlock every locked accountPass either a username or --all, not both.
Reset a forgotten password
Section titled “Reset a forgotten password”./taranac reset-password admin # reset, print a new random password./taranac reset-password admin --password 'S3cret!' # set an exact password./taranac reset-password admin --no-force-change # don't force a change at next loginBy default the account is required to change the password at the next login.
Recover or repair an admin login
Section titled “Recover or repair an admin login”When no admin login works at all, create-admin is a create-or-repair tool: it
creates the admin if missing, or repairs/re-enables and re-passwords an existing
one.
./taranac create-admin # use INITIAL_ADMIN_* from .env, print password./taranac create-admin opsadmin --email ops@example.com./taranac create-admin admin --password 'My$tr0ngPass'If --password is omitted a strong password is generated and printed; if the
username is omitted it falls back to INITIAL_ADMIN_USERNAME (or admin).
If the username is held by more than one account (a local and a directory
account, say), unlock, reset-password and create-admin refuse: they print
every account holding the name (id, source, LDAP configuration), change nothing
and exit non-zero. Remove or rename the extra accounts, then run the command
again. See One username, one account.
Licensing and clustering
Section titled “Licensing and clustering”Two subcommands support the Pro edition. installation-id only reads.
cluster spans everything from a status read to building a cluster and evicting
a node from it, so each destructive verb carries its own guard — the safe ones
(status, verify without --rehearse, connectivity, manifest-fetch,
manifest-info) can be run at any time. Both are refused on a witness or a
collector host.
Print the installation ID
Section titled “Print the installation ID”./taranac installation-id # print this install's immutable installation IDThe installation ID is the value a Pro license binds to. Send it to TaranacLabs
out-of-band to have a signed .lic issued, then upload that file under
Settings → Licensing. The same ID is shown on the Licensing page in the UI.
See Licensing & editions for the full flow.
High-availability cluster operations
Section titled “High-availability cluster operations”cluster is the whole HA interface: it builds the cluster as
well as operating it. You never hand-edit .env on each machine, copy TLS
material over scp or carry the master key by hand — and you never drive
ha-convert.sh / ha-join.sh yourself, though they are still in the bundle
because the wizard calls them.
./taranac cluster with no arguments prints one help text covering both
families, and every subcommand takes its own --help. This page is the syntax;
the procedure, the ordering rules and what each command proves are on
High availability.
./taranac cluster init --witness 10.0.0.3=wit-01 --node 10.0.0.2=node-02./taranac cluster add-witness --address 10.0.0.3./taranac cluster add-node --address 10.0.0.2./taranac cluster statusBuilding the cluster — run on the host holding the primary role, except
remove-witness (any database node) and sync / detach (the node concerned).
These rewrite host files and may recreate containers, so they run on the host, not
in a container. The topology commands take the cluster’s single update lock in
etcd, so one of them during another, or during a node’s update, is refused with
who holds it.
| Subcommand | Options |
|---|---|
init | --cluster NAME · --address ADDR · --name NAME (default <cluster>-node-1) · --witness ADDR[=NAME] · --node ADDR[=NAME] (repeatable) · --pg-allow-cidr LIST (default: the /24 of each address) · --force (recreate config/cluster.yml, discarding its skip: section) · --yes |
add-witness | --address ADDR (required) · --name NAME · --port N (9443) · --timeout N (1800 s) · --wait-seconds N (120) · --reinstall · --yes |
add-node | --address ADDR (required) · --name NAME · --port N (9443) · --timeout N (1800 s) · --ttl-minutes N (60) · --wait-seconds N (120) · --reinstall · --yes |
sync | --yes — run on the node the preflight reported stale; takes the cluster’s config/cluster.yml whole (keeping its own as config/cluster.yml.<time>.bak) and writes only this node’s DB_HOSTS / ETCD_HOSTS / ETCD_INITIAL_CLUSTER; recreates nothing |
remove-witness | --address ADDR (the witness, by name or address) · --forget ADDR (let a removed witness that will never run --leave go) · --yes (needed without a terminal) — on any database node; refused on a 1-node cluster (→ ./ha-deconvert.sh), on 2 database nodes, or when the voters left would not hold a majority; restarts nothing |
remove-node | --node NAME or --address ADDR · --yes (needed without a terminal). Given the witness’s name or address it hands over to remove-witness |
detach | --confirm FORK-MY-DATA (required) · --confirm NOT-A-MEMBER (in addition, when no cluster member answers) · --yes — turns this node back into a standalone install |
A node’s name is chosen at init or not at all: it is written into the etcd
ring, the certificates and every node’s .env, so there is no rename. Omit
=NAME and one is derived (<cluster>-node-N).
Operating the cluster — these run inside the api container, where the
database session, RBAC and the audit log are.
| Subcommand | Options |
|---|---|
status | — nodes, roles, replication lag and health |
verify | --rehearse (actually hand leadership over and back) · --to NAME (with --rehearse: the replica to hand over to; by default the one rehearsed longest ago, so repeated runs prove every replica) · --even-if-serving (rehearse on a cluster already carrying traffic) |
connectivity | --format text|tsv (default text) — the N×N matrix of every node’s proven login to every other node’s Postgres. Exits non-zero unless every pair is proven by a fresh login |
switchover | --to NAME (optional for a planned handover, required with --force) · --force · --confirm FAILOVER (required with --force — a forced promotion discards whatever the promoted node has not replayed) |
join-token | --name N (required) · --address A (required) · --ttl-minutes N (60) |
register | --token SECRET (required) — redeem a join token on the joining node |
revoke | --id ID (required) — revokes a pending join token, not a node |
decommission | --id ID or --name NAME (resolved among active nodes) — soft-deletes a roster row that is only a name, with no etcd or Patroni member behind it. A live member — the leader, an etcd member, a running Patroni member — is refused (and its button in the UI disabled): use remove-node |
license-check | — exit 0 when the HA license allows join tokens, 3 when it does not (for scripts) |
manifest-publish | reads config/cluster.yml on stdin and publishes it cluster-wide |
manifest-fetch | prints the published manifest on stdout; exit 5 if none has been published |
manifest-info | eval-able MP_* metadata — when it was published, how big, its sha256 |
The manifest subcommands are normally driven by the wizard: every topology command
publishes its change to config/cluster.yml before it makes it, and a node whose
copy differs, or that has none, takes the cluster’s copy and keeps its own as
config/cluster.yml.<time>.bak. The one thing you publish by hand is a skip:
entry you wrote into this node’s file — it is not merged otherwise:
./taranac cluster manifest-publish < config/cluster.yml # push this node's hand-edited copy./taranac cluster sync # take the cluster's copy on this nodeSee The cluster manifest.
The etcd certificates — these run in the host’s etcd-agent container, so they
work on any member, the witness included. Without a running agent they refuse,
change nothing, and print how to start it (./taranac up -d --no-deps etcd-agent) or
update the host.
| Subcommand | Options |
|---|---|
renew-cert | — renew this member’s etcd certificate now (members renew their own 45 days ahead anyway) · --all (run on any member: every member renews its own; one line per member with the old and new end date; exits non-zero unless every member renewed, naming each that could not) · --timeout N (for --all, default 120 s) |
rotate-ca | — replace the cluster’s etcd CA on every member, one at a time, nothing restarted · --yes (no [y/N] question; needed without a terminal) · --timeout N (seconds to wait for each member’s answer, default 90). Exit 0 done, 1 refused (nothing changed), 3 stopped part way — run it again, which first puts every member back on one CA |
rotate-ca refuses before it changes anything unless every member answers and runs
the same release. While it runs, update, add-node, add-witness, remove-node,
remove-witness and renew-cert --all are refused; after a run that stopped,
add-node and add-witness stay refused until a re-run completes it, and update
warns. See etcd certificates and the etcd CA.
Going back to one server, and leftovers. ./ha-deconvert.sh on the last database
node takes the cluster apart:
| Option | Effect |
|---|---|
--yes | No prompt |
--confirm NO-OTHER-NODES | Needed only when neither the etcd ring nor the node roster can be read; otherwise the script refuses while either lists another database node, and prints the remove-node for each |
--force | The last survivor no longer looks like the primary; never skips the other-database-node check |
--abandon | Undo a cluster init, a conversion or a join that never finished (removes this node’s etcd container and volume, keeps .env.bak-<time>); refused on a member and once Patroni has run on this node’s data |
--decommission-dead --node-name NAME | Take a dead node out of the ring and the roster; refused for a node that still answers |
Typed confirmation words. Commands that can lose data or split a cluster ask for
a word — typed in a terminal, or for most of them given with --confirm:
| Word | Asked by |
|---|---|
FORK-MY-DATA | cluster detach |
NOT-A-MEMBER | cluster detach and witness-join.sh --leave, when no member of the cluster answers |
NO-OTHER-NODES | ha-deconvert.sh, only when neither ring nor roster can be read |
FAILOVER | cluster switchover --force |
OUT-OF-ORDER | update --allow-out-of-order (--confirm OUT-OF-ORDER without a terminal) |
DOWNGRADE | update --version <older> on a cluster member (typed) |
DELETE-ALL-DATA | ./taranac down -v --confirm=DELETE-ALL-DATA — standalone only; down -v is refused outright on a cluster node and on the witness |
The other guards on a cluster — ./taranac up refusing a NODE_ADDRESS that is not
this host’s, install.sh on a member, a hand-edited TARANAC_VERSION — are listed
with their cures in Commands that refuse, and why.
Collecting diagnostics
Section titled “Collecting diagnostics”./taranac diagnose --protocol tacacs --device 10.0.0.5 # one device's traffic, decoded./taranac diagnose --protocol ncm --config "SW1 - show runn" # why a config collection fails./taranac diagnose --protocol snmp --device "core-sw-01" # why an SNMP poll comes back short./taranac diagnose --ha # this node's view of the clusterOne command, four collectors: a packet capture around one device with the daemon log,
Taranac’s own records and the generated config; a session recorder for one tracked
configuration; one recorded SNMP poll of one device, which writes nothing to the
database (see SNMP); and a cluster-state snapshot to run on every node, the witness included.
None of them writes a secret into what it produces — only 12-hex key fingerprints. The
packet-capturing collectors need root and tcpdump on the host (the virtual appliance
ships it): run them as root, or from an account that can sudo without a password,
which the script uses on its own. See
Diagnostic bundles.
The packet-capturing runs collect a few things more:
- Every run (
tacacs,radius,nac) writesipv6.txt: Docker’s version and whether its ip6tables is on, kernel IPv6,accept_ra, and for each published port of the daemon its IPv6 listener and Docker’s IPv6 DNAT. While IPv6 devices still arrive from the Docker gateway, it names the inventory entries that cover the gateway, as an[IPv6]finding in the summary. See IPv6. --protocol radiusand--protocol naccollect the radiusd supervisor’s traces: one per restart it caused (a crash, a crash loop, a rollback, a misroute), with the 24-hour count. The radiusd crashed family of alerts asks for exactly this command.--protocol nacalso shows EAP-TLS revocation on the node: the per-CA CRL and OCSP policy and the chain the daemon walks from each CA, the CRLs on disk compared with the database, the last day’s OCSP refusals by class, and a live probe of each OCSP responder. See Certificate revocation → Diagnosing.
| Option | Effect |
|---|---|
--protocol tacacs|radius|nac|ncm|snmp | which collector to run |
--ha | the cluster-state snapshot instead. Takes no --protocol and no --device, and is handled before any other argument is parsed |
--device IP | the device’s address as Taranac sees it (tacacs / radius / nac) |
--device NAME | for snmp: the device’s name in Taranac (or its id), not an address — which address is polled is one of the things the report explains |
--config NAME | the tracked configuration to diagnose (ncm), by name or id |
--duration SEC | capture for a fixed time instead of waiting for Enter |
--out DIR | where to write the archive or report (default ./diagnostics) |
--max-size MB | stop the capture past this size (default 100) |
--full-transcript | ncm only: keep recording past the first command. The report then contains the device configuration — share with care |
-h, --help | the built-in help |
--ha accepts its own two options instead: --out DIR (same default) and
--log-lines N (default 400).
Updating to a new release
Section titled “Updating to a new release”./taranac update is a true release upgrade: it refreshes both the container
images and the bundle files (the wrapper itself, docker-compose.yml,
install.sh, docs, .env.example). A plain docker compose pull only updates
images and silently leaves the bundle scripts stale — update closes that gap.
./taranac version # show installed version + check for a newer one./taranac update # update to the latest published version./taranac update --check # report only; change nothing| Option | Effect |
|---|---|
--version <X> | update to a specific version rather than the latest stable one |
--from <bundle>.tar.gz | apply a local bundle tarball (air-gapped); the version is read from the bundle’s own VERSION file, never from the file name (a --version that disagrees is ignored, with a warning) |
--check | report only, change nothing (same as ./taranac version) |
--repair | with --version <the version this node runs>: re-apply it — a new backup, the files swapped, the stack recreated |
--no-restart | swap the files and pin the version, but skip the image pull and restart. Apply later with ./taranac up -d |
--keep-images | keep the superseded images instead of reclaiming their disk |
--skip-image-verify | accept pulled images whose digests differ from the release’s IMAGES.lock — a registry mirror that re-pushed them |
--skip-space-check | pull even when the free-disk estimate says there is not enough room |
--allow-out-of-order | cluster repair only: skip the cluster’s update order, never its one-at-a-time lock. Asks for the word OUT-OF-ORDER; without a terminal add --confirm OUT-OF-ORDER. See Running it again, repairs and downgrades |
--ipv6-own-address-ok | go on although inventory entries that matched IPv6 devices through the Docker gateway stop matching them; without it the update names them and stops while the inventory has no IPv6 entry. TARANAC_IPV6_OWN_ADDRESS_OK=1 does the same, also for an older updater that does not know the flag. See IPv6 |
--yes, -y | answer the confirmation prompt — nothing else |
What update does, in order: checks the host (Docker 28.0 or newer, not Podman, IPv6
in the kernel, a dual-stack test network; accept_ra=2 where needed) and the device
inventory (gateway-covering entries; TACACS+ addresses that logged in without an entry,
listed and kept in .taranac-update.log), records the images the containers run now, checks
free disk space, pulls the new release’s images while the running version is
still pinned, compares each with the digest list the release ships
(IMAGES.lock) and reads each image’s files against its layers, replacing a damaged one
(Damaged images). Only then does it back up the current bundle files, swap in the
new ones, leave your .env and config/ untouched (it only lists any new
settings worth adding), bump the pinned version and restart, then remove the
superseded images — keeping the installed version and the one before it, and only
ever under this install’s own IMAGE_PREFIX. Database migrations run
automatically when the api container starts. A refused host check, or a failure while pulling or
verifying, leaves the node exactly as it was.
Things worth knowing before you run it:
- It never updates one half. If the bundle tarball cannot be fetched, the
update aborts with nothing changed rather than pulling images over a stale
framework — a release may need the new
docker-compose.ymlas much as the new images. A failed checksum aborts the same way. - A re-run on a node already on the version answers
Already on <version> — … Nothing to do.only when the running containers agree too; otherwise it finishes the interrupted update. It makes no backup and recreates nothing — it only starts containers that are stopped. Use--version <v> --repairto re-apply on purpose. - A plain
updatenever goes backwards. On a pre-release or a version newer than the latest stable release, it leaves the node as it is. - On a cluster run it on any member: it names the node to update next (every
replica, then the leader, then the witness), refuses on the wrong machine before
changing anything, and holds the cluster’s single update lock. Copy the printed
commands — they carry
--version <target>. See Upgrades under HA. - A witness updates only itself.
./taranac updateon an HA witness goes last, after every database node has finished (it waits for them, and refuses naming one that has not), writes exactly one line —TARANAC_VERSION=<v>— into the witness.env, applies a changeddocker-compose.witness.ymlitself on a healthy etcd ring, and starts no Taranac container. A witness that ran--leaveis reported as having left its cluster, with nothing to update. - A collector host is not moved from here either — move a collector with
./collector-join.sh --update, which asks its core what version to be.
Air-gapped hosts. A failed online check is a notice, not an error — normal
operation never touches the network. To update offline, copy the bundle tarball
onto the host and point update at it:
./taranac update --from taranac-bundle-1.3.2.tar.gzIf a bundle is so old it has no update command yet, copy taranac and
taranac-update.sh from a newer bundle once; from then on it is self-maintaining.
Factory reset (demo and dev only)
Section titled “Factory reset (demo and dev only)”./taranac reset truncates every table, re-applies the default seeds (settings,
RBAC roles, AAA templates and profiles, the default policy), regenerates the
daemon configs, and recreates the admin from the INITIAL_ADMIN_* values in
.env. It is meant for demo and lab stands, and it is guarded so you cannot fire
it by accident.
./taranac reset # dry run — print the plan, change nothing./taranac reset --confirm RESET # actually perform the reset| Flag | Effect |
|---|---|
--confirm RESET | Required to actually wipe. Without it, reset only prints the plan. |
--no-backup | Skip the automatic pre-reset backup and its abort-on-failure guard. |
--force-prod | Override the environment guard so reset runs outside demo/development. Dangerous. |
Two guards must both pass:
- Environment — only
APP_ENV=demoordevelopmentmay reset. On anything else (production) it refuses unless you add--force-prod. To reset a production-flagged stand deliberately, setAPP_ENV=demoin.env, run./taranac up -dto recreate theapicontainer, then reset. - Typed confirmation — nothing happens without
--confirm RESET.
By default a backup is taken before the wipe and the reset aborts if that
backup fails, so you are never left without a safety net. --no-backup removes
both.
Rotating the master key
Section titled “Rotating the master key”./taranac rotate-key replaces the key that encrypts every stored secret —
device and group shared secrets, enable passwords, the internal CA’s private
keys, NCM credentials, TOTP secrets, the LDAP bind password, the license. It runs
on a stopped system, in one database transaction, so “half the data on the
old key” cannot happen, and it keeps the previous key under
config/retired-keys/ so archives taken before the rotation stay restorable.
./taranac rotate-key --confirm LOCAL-ACCESS-VERIFIED./taranac rotate-key --confirm LOCAL-ACCESS-VERIFIED --new-key-file /path/to/key| Flag | Effect |
|---|---|
--confirm LOCAL-ACCESS-VERIFIED | Required. Typed in full, and nothing happens without it. |
--new-key-file <path> | Use a key you supply instead of a generated one. |
--current-key-file <path> | Read the current key from a file instead of the interactive prompt. |
--no-backup | Skip the backup taken before the rotation (not recommended). |
-h, --help | The built-in help. |
You are asked for the current key even when it is already in .env. That is
not a formality: it proves you still hold the key that opens every archive taken
so far.
Disaster recovery (backup restore)
Section titled “Disaster recovery (backup restore)”Restore is not in the UI — it erases the current configuration — so it runs from the CLI, off the API, and verifies your master key before it touches anything. One command drives both phases:
./taranac restore /root/taranac-backup-YYYYMMDDTHHMMSSZ.tar # a file on this host./taranac restore taranac-backup-YYYYMMDDTHHMMSSZ.tar # already in the backups volume./taranac restore <archive> --master-key-file /root/old.key # this install runs a different key./taranac restore <archive> --yes # skip the phase-2 confirmation| Argument | Effect |
|---|---|
<archive> | The archive’s path on this host — relative to where you run the command, or absolute — which is copied into the backups volume; or the file name of an archive already in that volume |
--master-key-file <path|-> | Open the archive with the key in that file (nothing else in it), when this install does not already run the original one; - reads it from stdin. The key is written into .env |
TARANAC_MASTER_KEY | The same, from the environment. The file wins if both are given |
--master-key | Refused, with nothing changed: a key on the command line sits in the shell history and in ps |
--yes, -y | Skip the interactive confirmation before the destructive phase 2. Required without a terminal (piped, ssh without -t, or the key read from stdin): without it, restore refuses before it changes anything and prints the command with --yes |
-- | Ends the options, for an archive name that starts with - |
-h, --help | The built-in help |
The script refuses up front an install whose key is not read from .env
(MASTER_KEY_SOURCE other than env) and, under HA, any node but the leader.
Under HA it also never puts an older etcd CA from the archive over the cluster’s
current one: that copy is set aside in config/etcd-ca.restored-<time>.
Phase 1 verifies the key and extracts the archive’s host files and certificates.
The script then overlays the five values that must survive a rebuild —
MASTER_KEY, SECRET_KEY, TARANAC_MFA_API_KEY, INTERNAL_API_KEY,
REPORTS_RENDERER_API_KEY — onto this host’s .env, keeping the previous file as
.env.pre-restore.<UTC time>, and checks that the key now in .env opens the
archive: if not, .env is put back and nothing else has changed. It restores the
certificates and ./config, stops the api and loads the database from a one-off
container (phase 2), clears the daemons’ master_key volume so their entrypoints
adopt the restored key, recreates the services that read those secrets, waits for
the api to be healthy, restarts everything, and removes the decrypted extraction.
If the load fails, it tells you whether the data went in; re-running the same
command is safe.
Two things the archive never carries: MFA enrollments (taranac-mfa has its own database and its own crypto) and the Active Directory machine account (Samba rotates its password weekly, so a restored one would be a broken trust — re-join the domain from System → Authentication if MS-CHAPv2 is used).
The phases are also reachable individually inside the api image, which is what
the script calls: python -m app.scripts.restore host <archive> [--master-key -] [--out DIR] (default /restore; - reads the key from stdin), python -m app.scripts.restore check-key <archive> (one key on stdin; exit 0 it opens the
archive, 10 not a key, 11 another key) and python -m app.scripts.restore db <archive> [--yes] [--force], where --force overrides a version or schema
mismatch. Phase 2 runs in a one-off container (./taranac run --rm --no-deps -T api …) with the api stopped, because the api will not start on the restored key
until the dump is in.
For a non-standard layout, point the script at the right pair explicitly rather
than passing a positional: it reads TARANAC_ENV_FILE and
TARANAC_COMPOSE_FILE, defaulting to .env and docker-compose.yml.
Under HA the script detects the cluster and merges the Patroni overlay by itself.
It runs on the leader only, refuses an archive cut under other keys than the
cluster runs now (before a rotate-key), and keeps the cluster’s live roster and
published manifest. See
Backup & upgrades
for the full runbook.
In-container watchers (background, not commands)
Section titled “In-container watchers (background, not commands)”You will not run these yourself, but it helps to know they exist when reading logs. The TACACS+, RADIUS and NAC containers each run small watcher scripts that keep the running daemon in sync with the configuration the web UI produces. The admin UI generates a config file, drops a signal file next to it, and a watcher picks up the change:
| Watcher (in each daemon container) | Role |
|---|---|
reload_watcher.sh | Waits on a .reload_signal file; on TACACS+ sends SIGHUP to reload; on RADIUS/NAC validates with radiusd -XC first, then decides between a SIGHUP and an in-place restart. |
restart_class.sh (RADIUS/NAC) | Defines the restart class — clients.conf, radiusd.conf, sites-enabled/aaa, dictionary — and hashes it. FreeRADIUS applies those only on a full restart; a SIGHUP would silently no-op them. When the hash changes the watcher restarts the supervised radiusd in place (no container churn); otherwise it hot-reloads. |
validate_watcher.sh | Runs a dry-run validation of a pending config and writes the result back so the UI can show whether a change is valid before applying it. |
health_watcher.sh | Reports daemon health used by the container healthcheck. |
log_trimmer.sh / trim_log.py | Trim daemon log files to keep them bounded. |
dictionary_index.py (RADIUS/NAC) | Indexes the dictionaries FreeRADIUS actually ships, so the settings UI can tell you which attribute names are already taken. |
The effect for an operator: when you save an AAA, NAC or device change in the UI,
the relevant daemon reloads on its own within seconds — there is no command to
run and no restart. If a reload ever doesn’t take, ./taranac restart <service>
(for example radius or nac) forces a clean restart.
Quick reference
Section titled “Quick reference”| Command | Purpose |
|---|---|
sudo bash bootstrap.sh | Install Docker + compose, add user to docker group (once, as root) |
./install.sh | First install (generate secrets, start stack); re-run on a standalone server = image refresh |
./taranac ps | Service status |
./taranac logs -f <service> | Follow a service’s logs |
./taranac up -d / ./taranac down | Start / stop the stack |
./taranac restart <service> | Restart one service (e.g. edge after a new cert) |
./taranac unlock [<user>|--all] | List or unlock locked admin accounts |
./taranac reset-password <user> | Reset a forgotten admin password |
./taranac create-admin [<user>] | Create or repair an admin login |
./taranac installation-id | Print the installation ID to obtain a Pro license |
./taranac cluster | Print the HA help — both the building and the operating family (Pro) |
./taranac cluster init|add-witness|add-node | Build an HA cluster (Pro) |
./taranac cluster status|verify|connectivity | Cluster health, survivability and the login matrix (Pro) |
./taranac cluster switchover [--to <name>] | Hand leadership over on purpose (Pro) |
./taranac cluster remove-node --node <name> | Evict a node; detach --confirm FORK-MY-DATA for this one (Pro) |
./taranac cluster remove-witness | Retire the arbiter once there are 3+ database nodes (Pro) |
./taranac cluster renew-cert [--all] | Renew this member’s etcd certificate now, or every member’s (any member, witness included) |
./taranac cluster rotate-ca | Replace the cluster’s etcd CA on every member, nothing restarted |
./taranac diagnose --protocol <p> --device <ip> | One device’s traffic, logs, config and records in one archive |
./taranac diagnose --protocol snmp --device <name> | One recorded SNMP poll of one device, explained layer by layer |
./taranac diagnose --ha | This node’s view of the cluster — run it on every node and the witness |
./taranac captive-portal status|datapath|restart|update|reset-trust|uninstall | Operate the standalone captive portal installed on this host |
./taranac version | Show installed version, check for a newer one |
./taranac update | Upgrade images and bundle files to the latest (on a cluster: any member, it names the next) |
./taranac rotate-key --confirm LOCAL-ACCESS-VERIFIED | Rotate the master key on a stopped system, in one transaction |
./taranac reset --confirm RESET | Factory reset (demo/dev only) |
./taranac restore <path to archive> | Guided disaster recovery from a backup |
When to use what
Section titled “When to use what”| Situation | Use |
|---|---|
| Routine admin locked out after failed logins | ./taranac unlock <user> |
| Admin forgot their password | ./taranac reset-password <user> |
| No working admin at all (e.g. RBAC mistake) | ./taranac create-admin |
| Pull a newer image of the same release (standalone) | re-run ./install.sh |
| Move to a new product version | ./taranac update |
| Wipe a demo stand back to factory state | ./taranac reset --confirm RESET |
| Recover a real platform after data loss | ./taranac restore <path to archive> (never reset) |
| Device administration or 802.1X stopped working | ./taranac diagnose --protocol <p> --device <ip> |
| The cluster disagrees with itself | ./taranac diagnose --ha on every node, witness included |
| SNMP polling fails or a forwarding table comes back empty | ./taranac diagnose --protocol snmp --device <name> |
| The master key was exposed, or you rotate on a schedule | ./taranac rotate-key --confirm LOCAL-ACCESS-VERIFIED |
Related
Section titled “Related”- Installation & first run — full deploy runbook
- Backup & upgrades — taking backups before upgrades and resets, the full restore and key-rotation runbooks
- Diagnostic bundles — what each
diagnosecollector gathers and how to read it - Settings reference — the
.envsettings these commands read - Licensing & editions — what
installation-idis for and how to activate Pro - High availability — the cluster the
clustersubcommand operates on - Deployment bundle & releases: github.com/TaranacLabs/taranac