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. - 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)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, sync | cluster-wizard.sh on the host |
cluster remove-node, cluster detach | cluster-remove.sh on the host |
diagnose | diagnose.sh — --ha re-execs diagnose-ha.sh |
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 NCM collector — that is how those hosts get their own join
scripts, and it puts this wrapper on all three. Since 1.2.7 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. ./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. |
./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 two 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),
and -h / --help. Anything else is refused by name.
install.sh is also the upgrade path for the images: re-running it when .env
already exists never regenerates secrets — it just pulls and restarts. For a
full release upgrade (images and bundle files) use ./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 NCM
collector, a remote 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> | --env-file <path> (default ./.env) · --print-auth (print the derived auth header and exit — a diagnostic for “invalid credentials”) |
collector-join.sh | --core <https url> --enrollment-token <secret> | --ca-file <path> | --trust-any · --collector-id <label> · --poll-interval <sec> · --status · --reset-trust · --update (with --check, --version <X> or --from <image>.tar) · --uninstall (with --force to discard a non-empty outbox) |
portal-join.sh | --core <https url> --token <portal-token> | --ca-file <path> | --trust-any · --dir <dir> · --status · --reset-trust · --uninstall |
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.
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).
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”Since 1.2.3 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. These
rewrite this host’s .env and recreate containers, so they run on the host, not
in a container.
| 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; reconciles config/cluster.yml and then DB_HOSTS / ETCD_HOSTS |
remove-node | --node NAME or --address ADDR · --yes |
detach | --confirm FORK-MY-DATA (required) · --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) · --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 |
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. They are first-class
commands because they are the two cures the wizard prints when a node’s copy of
config/cluster.yml and the cluster’s copy disagree — it names both and never
picks one for you:
./taranac cluster manifest-publish < config/cluster.yml # this node's copy is right./taranac cluster sync # the cluster's copy is rightCollecting 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 --ha # this node's view of the clusterOne command, three 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; 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: run them as root, or from an account that can
sudo without a password, which the script uses on its own. See
Diagnostic bundles.
| Option | Effect |
|---|---|
--protocol tacacs|radius|nac|ncm | 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) |
--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 (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 |
--from <bundle>.tar.gz | apply a local bundle tarball (air-gapped); the version is derived from the filename if --version is not given |
--check | report only, change nothing (same as ./taranac version) |
--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 |
--allow-out-of-order | continue past the cluster-readiness refusal (see Upgrading a cluster) |
--yes, -y | skip the confirmation prompt |
What update does, in order: backs up the current bundle files, swaps in the new
ones, leaves your .env and config/ untouched (it only lists any new
settings worth adding), bumps the pinned version, pulls images and restarts, then
removes 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.
Two 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 witness is not a Taranac node. Since 1.2.7,
./taranac updateon an HA witness refreshes the bundle’s files (witness-join.sh, the witness compose file,diagnose.sh, the HA runbook) and stops: it neither reads nor edits the witness.env, and it starts nothing. A collector host behaves the same way — 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.2.8.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 taranac-backup-YYYYMMDDTHHMMSSZ.tar./taranac restore <archive>.tar --master-key <KEY> # this install runs a different key./taranac restore <archive>.tar --yes # skip the phase-2 confirmation| Argument | Effect |
|---|---|
<archive>.tar | The archive filename, as it sits in ./backups — the api container reads it from that same mounted volume. |
--master-key <KEY> | Open the archive with an explicit key, when this install does not already run the original one. |
--yes, -y | Skip the interactive confirmation before the destructive phase 2. |
-h, --help | The built-in help. |
Phase 1 verifies the original master 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; clears the daemons’ master_key volume
so their entrypoints adopt the restored key; restores the certificates and
./config; recreates the services that read those secrets; and only then runs
phase 2, which replaces the configuration with the archive’s. Everything finishes
with a full restart so each service re-reads what was restored.
./backups/restore holds decrypted secrets — delete it when you are done. 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 two phases are also reachable individually inside the api container, which
is what the script calls: python -m app.scripts.restore host <archive> [--master-key KEY] [--out DIR] (default /restore) and python -m app.scripts.restore db <archive> [--yes] [--force], where --force overrides a
version or schema mismatch.
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; recovery runs on the leader, and the other nodes rejoin afterwards. 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 = 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 diagnose --protocol <p> --device <ip> | One device’s traffic, logs, config and records in one archive |
./taranac diagnose --ha | This node’s view of the cluster — run it on every node and the witness |
./taranac version | Show installed version, check for a newer one |
./taranac update | Upgrade images and bundle files to the latest |
./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 <archive>.tar | 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 | 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 <archive>.tar (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 |
| 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