Перейти к содержимому

Installation

Taranac ships as a set of Docker images orchestrated by a small deployment bundle. You do not build anything from source: you download the bundle, run two scripts, and the installer generates every secret, pulls the images, and brings the whole stack up behind a TLS reverse proxy. This page walks through a clean install on a single host and the first things you do afterwards.

The bundle contains only configuration — a Docker Compose file, an installer, a taranac operations wrapper, and a config/ directory you drop your TLS certificate and (optionally) Firebase key into. The application itself arrives as images pulled at install time.

There is no source checkout and no private registry anywhere in this: the images come from the public ghcr.io/taranaclabs/taranac, the bundle from the public TaranacLabs/taranac repository, and both ship under the Elastic License 2.0. There is no separate “Pro” build either — one set of images and one bundle serve every installation, high availability included; Pro is a license you upload later, not a different download.

Run the bundle’s two scripts; the installer generates secrets and pulls images, and the whole container stack comes up behind a single TLS edge.
RequirementMinimum
OSUbuntu 22.04 / 24.04 / 26.04 LTS (any modern Linux with systemd works)
CPU4 cores
RAM6 GB
Disk20 GB free
Accessa non-root user with sudo
SoftwareDocker Engine 28.0 or newer with its ip6tables on (the default) + the Docker Compose plugin, and openssl (plus curl and tar, which ./taranac update uses later). Podman answering the docker command is not supported
KernelIPv6 support on — not booted with ipv6.disable=1 (IPv6 turned off by sysctl is fine). The host itself needs no IPv6 address

bootstrap.sh, install.sh and ./taranac update check Docker and the kernel before they change anything. They refuse an older Docker, Podman, a kernel without IPv6, or a Docker that cannot create a network with IPv6 beside IPv4, each with the command that fixes it; with Docker’s ip6tables turned off they warn, because IPv6 clients are then seen with the Docker gateway’s address. On a host whose IPv6 address or route comes from router advertisements they also set accept_ra=2. When the host passes they say so in one line, for example Host: Docker 29.1.2 ✓, kernel IPv6 ✓, dual-stack network test ✓, accept_ra: nothing changed. See IPv6.

You also need the hostname or IP clients will use to reach Taranac. Pick the primary one before you start — the installer asks for it, and it becomes the name in links, in the printed URL and in the certificate’s subject. It does not have to be the only address the node answers on: adding the others is a post-install setting (see Answering on more than one address), not a question you have to get right before the product has ever run.

Open inbound only the ports for the protocols you actually use. The admin UI and API are always reached through port 443. Every port answers IPv4 and IPv6 alike, and an IPv6 client is seen with its own address — see IPv6.

PortProtocolUsed for
443 / 80TCPAdmin UI and API (80 redirects to 443)
49TCPTACACS+ (device administration)
6049TCPTACACS+ over TLS
1812 / 1813UDPRADIUS authentication / accounting (AAA)
1814 / 1815UDPNAC 802.1X authentication / accounting
8445TCPEST gateway — the device enrollment URL https://<host>:8445/.well-known/est: first enrollment by password and renewal with the current certificate; also SCEP over HTTPS. See EST enrollment
8446TCPSCEP over plain HTTP (SCEP paths only). Published on every installation, whether SCEP is on or not. See SCEP enrollment
8080 / 8444TCPCaptive portal (guest / onboarding), HTTP and HTTPS
8443TCPMFA push service — where phones reach push registration, unless you front it on 443 with TARANAC_MFA_DOMAIN

Change of Authorization needs no inbound port on Taranac: the api container sends CoA and Disconnect requests to the switch, on the switch’s own CoA port (3799 by default, set per device, group, vendor dictionary or globally). Allow that direction from Taranac to the device instead. See CoA settings.

Every one of those is a default that .env overrides (TACACS_PORT, TACACS_TLS_PORT, RADIUS_AUTH_PORT, RADIUS_ACCT_PORT, NAC_AUTH_PORT, NAC_ACCT_PORT, EST_GATEWAY_PORT, SCEP_HTTP_PORT, MFA_PORT, CAPTIVE_PORTAL_HTTPS_PORT, …).

The TACACS+, RADIUS and NAC ports are one number end to end: the value in .env is the port published on the host, the port the daemon listens on inside its container, and the port the settings and status pages show. To change one, edit it in .env and recreate the stack:

Terminal window
./taranac up -d

Devices must then be pointed at the new port. Keep RADIUS and NAC off 18120 and 18140 (the daemons’ internal status listeners), and give authentication and accounting different numbers. On an HA cluster each node reads the ports from its own .env, so keep them equal on every node.

Outbound HTTPS is needed to pull the container images (and, periodically, the IEEE OUI database used to identify endpoint vendors).

Taranac is a multi-container stack on one shared PostgreSQL database. A single public edge reverse proxy terminates TLS and is the only browser-facing service; everything else talks over an internal Docker network.

ServiceRole
edgePublic TLS reverse proxy — the certificate browsers see
frontendReact admin UI (internal, behind the edge)
apiFastAPI control plane; generates the daemon configs
taranac-mfaPush / TOTP multi-factor authentication service
tacacsTACACS+ daemon (device administration)
radiusRADIUS daemon (AAA)
nacRADIUS daemon for 802.1X port access control
est-gatewayDevice certificate enrollment: EST and SCEP over HTTPS (port 8445), SCEP over plain HTTP (port 8446)
captive-portalGuest / onboarding portal
taranac-rendererHTML→PDF for report exports (internal only, no published port)
winbindThe Active Directory machine account MS-CHAPv2 needs — inert until a domain is configured, so it is safe on every install
postgresPostgreSQL 16 database

The api container is the brain: when you change policy in the UI it regenerates the TACACS+, RADIUS, NAC, and captive-portal configuration, writes it to shared volumes, and signals the daemons to reload — no restarts, no hand-edited config files. The protocol daemons run in separate containers so a fault in one engine cannot take down the others.

All services run with restart: unless-stopped and Docker is enabled on boot, so the whole stack comes back automatically after a reboot.

The containers share one Docker network, taranac_default, pinned to 10.255.255.224/27 so Docker never picks a range your LAN uses. The edge, the UI, the daemons, the portal, the renderer, winbind and the EST gateway have fixed addresses in its lower half; Docker hands out every other address from 10.255.255.240/28. That /28 is the source the HA host-firewall rule admits — see The node’s own Docker network. The same network carries the IPv6 ULA fd64:1fbc:c16e::/64 for the services clients dial — edge, taranac-mfa, tacacs, radius, nac, est-gateway and captive-portal; every other container is kept off IPv6. See IPv6.

Download and unpack the latest release. There’s no version to pick — latest always resolves to the newest bundle — and the archive expands into its own taranac/ directory:

Terminal window
curl -fsSL -O https://github.com/TaranacLabs/taranac/releases/latest/download/taranac-bundle.tar.gz
tar xf taranac-bundle.tar.gz
cd taranac

Prefer to track the bundle scripts with git instead? git clone https://github.com/TaranacLabs/taranac.git && cd taranac gives you the same deployment files (the clone contains no product source — the app still arrives as images pulled at install time).

The bundle is a flat archive of deployment files — the install.sh and bootstrap.sh scripts, the taranac operations wrapper, docker-compose.yml and the config/ templates. All releases are published on the project releases page.

SECURITY.md in the bundle says how to report a vulnerability: by email to security@taranac.pro, never in a public issue, with the response times you can expect and how disclosure is coordinated.

If the host already has Docker Engine and the Compose plugin, skip this. Otherwise the bundle ships a bootstrap script:

Terminal window
sudo bash bootstrap.sh

It installs Docker, the Compose plugin, enables the engine on boot, and adds your user to the docker group. A Docker already on the host is kept — unless it is older than 28.0 or is Podman: then the script stops before changing anything and prints the command that updates or replaces it on this host.

Terminal window
./install.sh

The installer asks just three questions (press Enter for the default in brackets):

PromptDefaultNotes
Primary domaintaranac.example.comHostname/IP for the admin UI; becomes the certificate’s subject and the printed URL
Initial admin usernameadminThe first web-admin account
Initial admin emailadmin@<domain>

It does not ask which other addresses the certificate should cover: nothing at install time depends on the answer, because the certificate generated here is self-signed whatever addresses it carries. Widening it is a post-install setting, below.

All three answers can be pre-seeded through the environment for an unattended install — TARANAC_DOMAIN, INITIAL_ADMIN_USERNAME, INITIAL_ADMIN_EMAIL. That is how the appliance’s first-boot wizard drives the same installer.

Everything else is a bundle property, not an operator choice: the image registry (ghcr.io/taranaclabs/taranac) and the version tag are fixed by the release you downloaded — the tag is read from the bundle’s VERSION file, so the installer never asks for it and the images can’t drift from the bundle. If you mirror the images (air-gapped installs) or want to pin the MFA push to its own hostname, pass them as environment variables instead of answering a prompt:

Terminal window
IMAGE_PREFIX=registry.local/taranac TARANAC_MFA_DOMAIN=mfa.example.com ./install.sh

With no MFA push domain set, push registration defaults to https://<primary domain>:8443/ttype/push. Set one and the edge fronts the push service on 443 for that hostname instead. SKIP_PULL=1 is the other useful override: it uses images already loaded on the host rather than pulling any.

The installer then generates every secret for you — the PostgreSQL password, the JWT signing key (SECRET_KEY), the encryption master key (MASTER_KEY), the internal API keys (the EST gateway’s EST_PROXY_KEY among them), the replication and Patroni credentials a later HA conversion will need, and a strong admin password — writes them to a .env file, pulls the images, and starts the stack.

When it finishes it prints the admin login: URL, username, and the generated password. (It is also in .env as INITIAL_ADMIN_PASSWORD if you miss it.) Under Network devices the same screen prints the endpoints devices use by address: the TACACS+ over TLS listener (<node IP>:6049), the EST gateway (https://<node IP>:8445) and SCEP over plain HTTP (http://<node IP>:8446/scep). The first two present certificates that name the node’s address.

That node IP is TARANAC_NODE_IPS in .env, which the installer detects from the host’s default route (and ./taranac update adds when it is missing). It feeds the certificates Taranac generates for those two listeners, together with TARANAC_DOMAIN and, on a cluster node, NODE_ADDRESS: they are signed by the Infrastructure CA, name these addresses, and are reissued at start-up when the addresses change or the certificate is within 30 days of expiry. A certificate you deployed yourself is never replaced. If the node’s address changes, edit TARANAC_NODE_IPS (comma-separated) and run ./taranac up -d. See TACACS+ over TLS and EST enrollment.

The first boot is allowed to be slow: the api health check gives it up to five minutes (migrations on an empty database, on modest hardware) before a failed check counts. The manufacturer (OUI) table is not part of that wait — it is filled in the background once the API answers, from the snapshot inside the image first and then from the IEEE, so an install on a closed network starts just the same. See OUI database.

The node is named taranac-node-1 in .env (TARANAC_NODE_NAME). That name is the HOSTNAME of every syslog message the node sends, so if several standalone servers report to one SIEM, give each a distinctive one — see The HOSTNAME field.

.env also sets APP_ENV=production, which is the default when the line is missing. Leave it there: development pre-fills the login page with the initial admin credentials for anyone who opens it, and only development or demo allow ./taranac reset.

.env is written 0640 and given group 999 — the gid the api container runs as — and config/tls, config/firebase and config/etcd-ca get the same grant. That one grant is what makes a backup complete: the api reads those files through a read-only mount to put them in the archive, and nothing inside the container can widen a host file it cannot read. Left 0600 and owned by the operator, the file would make every backup fail.

Changing a file’s group needs root, so on a non-root install the script may ask for your sudo password once, saying why first. It is genuinely once: ./taranac update preserves the grant on every later run — and repairs it on an older installation that has never re-run install.sh.

The master key (a Fernet key) is the root of all secret encryption. The installer puts it in .env as MASTER_KEY with MASTER_KEY_SOURCE=env, which is the simplest model: the key lives in the environment file and is loaded at start. Without it the API still starts but runs sealed — any operation touching an encrypted field fails until the key is supplied. Treat .env as a sensitive credential and store a copy somewhere safe and offline — backup archives are encrypted with this key, so a copy that lives only on the host you are protecting is not a copy. Never edit the value by hand; ./taranac rotate-key is the supported way to change it deliberately, and it keeps the retired key so older archives stay restorable.

Open https://<your-domain>/ and sign in with the printed admin credentials.

Taranac dashboard after first login The dashboard is the landing page once you are authenticated.

Until you install a trusted certificate (next step), the edge serves a temporary self-signed certificate, so the browser shows a security warning — that is expected on a fresh install. Change the admin password right after logging in.

Taranac does not automate Let’s Encrypt — you bring your own certificate (commercial CA, your organization’s internal CA, or a manually issued one). Drop two files into config/tls/, using exactly these names:

FileContents
tls.crtFull chain — your server certificate followed by any intermediates
tls.keyThe matching private key (unencrypted PEM)

The certificate’s Subject Alternative Name must cover your primary domain, every address listed in TARANAC_EXTRA_ADDRESSES, and the MFA push domain if you set one — with any IP address present as an IP: SAN, not DNS:. Then apply it without rebuilding anything:

Terminal window
./taranac restart edge

Reload the page and the warning is gone. If files are absent the edge keeps serving its self-signed certificate, so the stack always boots either way. At startup the edge names in its log every address your certificate does not cover, so ./taranac logs edge is the check that it landed.

That self-signed fallback lives in a Docker volume, not the container’s writable layer: it is minted once and reused, and regenerated only when it is missing, within 30 days of expiring, or no longer covering this node’s addresses. Restarts and upgrades leave it alone — which is what keeps a standalone collector or a DMZ captive portal that pinned it working, instead of needing a re-pin at every remote site after a routine restart edge.

The primary domain is rarely the only way in: staff open a DNS name while a remote collector or a DMZ captive portal dials this node’s IP, and both have to work without picking a winner. List the rest in .env — names and IPs, comma-separated:

Terminal window
TARANAC_EXTRA_ADDRESSES=taranac.corp.example,10.20.0.10

They widen both the certificate’s SAN and the edge’s nginx server_name; the primary stays the one used for links and the printed URL. An IP goes into the certificate as an IP: SAN rather than a DNS: one — a distinction a browser merely warns about, while a collector or DMZ portal refuses the connection outright. That failure surfaces at the remote site with nothing pointing back here, which is why it is worth getting right the first time.

Apply it by recreating the edge:

Terminal window
./taranac up -d edge

Not ./taranac restart edge: a restart reuses the container it already has, with the environment it was created with, so a value you just added to .env would never be read. (Certificate files are different — those are read from a mount at every start, so a plain restart is enough for them.) Recreating also re-mints the self-signed certificate to cover the new address, so any module that pinned the old one has to be re-pinned.

Out of the box, MFA works in free poll-only mode — the app checks for pending approvals periodically. To enable instant push notifications, drop your Firebase service-account JSON at config/firebase/firebase-credentials.json, then:

Terminal window
./taranac up -d taranac-mfa

Scaling to high availability (optional, Pro)

Section titled “Scaling to high availability (optional, Pro)”

A single host is the default and the right choice for most deployments. If you later need authentication to survive the loss of a node, the same bundle converts into a multi-node high-availability cluster — Patroni-managed PostgreSQL streaming replication with automatic failover. That is three commands rather than a runbook:

Terminal window
./taranac cluster init # declare the cluster
./taranac cluster add-witness --address <addr> # the etcd arbiter — REQUIRED first
./taranac cluster add-node --address <addr> # once per additional node

Each prints the single command to run on the machine being added. A witness — an etcd arbiter on a third host — is required, not optional. HA is a Pro feature, unlocked by a license that also sets how many nodes you may run; it is the same images and the same bundle, so nothing about this install has to change in advance. Start single-host and convert when required.

See High availability for the architecture, prerequisites and the step-by-step conversion, and Licensing for enabling the Pro feature.

Use the taranac wrapper from the bundle directory — a shortcut for the full docker compose --env-file .env -f docker-compose.yml … invocation, so any Compose subcommand works too. It is a little more than a shortcut: it resolves what kind of host it is on (a product node, an HA witness, a standalone collector) and drives that host’s compose file, merging the Patroni overlay on a converted node. Every subcommand is catalogued in the CLI reference.

Terminal window
./taranac ps # service status
./taranac logs -f api # follow a service's logs
./taranac restart edge # restart one service (e.g. to apply a new cert)
./taranac down # stop everything (data is preserved in volumes)
./taranac up -d # start again

After MAX_LOGIN_ATTEMPTS failed logins (5 by default) an account is locked. The lock does not auto-expire, so a locked-out admin cannot recover through the web UI — common on an internet-facing host once someone starts guessing passwords. Recover from the server console:

Terminal window
./taranac unlock # list locked accounts
./taranac unlock admin # unlock one account
./taranac unlock --all # unlock every locked account
./taranac reset-password admin # reset a forgotten password (prints a new one)
./taranac create-admin # create-or-repair an admin login when none works

On a standalone server, re-running the installer refreshes the images of the bundle’s version: when .env already exists, install.sh never regenerates secrets — it backfills any secret a newer bundle introduced, repairs the .env and config/ permissions, runs the same host checks and inventory checks as ./taranac update, pulls the images and restarts. On an HA cluster node or a witness it refuses, changes nothing and names ./taranac update — re-running it there would restart the node outside the cluster’s update order and lock. A release upgrade is ./taranac update, which refreshes both the images and the bundle files (wrapper, Compose files, installer, docs):

Terminal window
./taranac version # installed version + check for a newer one
./taranac update # update to the latest (or: --version <X>)

update leaves your .env and config/ untouched, bumps the pinned version and recreates the stack; database migrations run automatically when api starts. Things it does on the way past that are worth knowing:

  • It lists the settings this release added, as a notice rather than a warning — their defaults already apply, so there is nothing to do.
  • It reclaims disk. Once the stack is back up, superseded images are removed, keeping the version just installed and the one before it as a rollback target. --keep-images opts out.
  • It pulls and checks before it changes anything. The new images are pulled and compared with the digest list the release ships while the old version still runs, and each image’s files are read against its layers; a failed pull, a full disk, a wrong digest or a damaged image that cannot be replaced leaves the node as it was.
  • It checks the host first — Docker 28 or newer, IPv6 in the kernel, a dual-stack test network — and sets accept_ra=2 where the host lives on router advertisements.
  • It lists TACACS+ addresses that will be refused. TACACS+ answers only inventoried devices; the update names the addresses that logged in without an inventory entry in the last 30 days and keeps the list in .taranac-update.log. See Only inventoried devices log in.
  • On a cluster, it names the node to update next — every replica, then the leader, then the witness — and refuses on the wrong machine before it writes anything. See Upgrades under HA.

Take a backup before upgrading — Backups & upgrades covers the whole update model, every flag, and the restore path.

You want to…Do this
Install on a fresh hostbootstrap.sh (if no Docker) → install.sh
Apply a new TLS certificateDrop files in config/tls/, then ./taranac restart edge
Answer on another name or IPAdd it to TARANAC_EXTRA_ADDRESSES in .env, then ./taranac up -d edge
Change the TACACS+, RADIUS or NAC portEdit it in .env, then ./taranac up -d; point the devices at the new port
The node’s IP address changedEdit TARANAC_NODE_IPS in .env, then ./taranac up -d
Move to a newer version./taranac update
Convert to an HA cluster./taranac cluster init → add-witness → add-node
Stop / start the stack./taranac down / ./taranac up -d
Wipe a standalone install’s data for good./taranac down -v --confirm=DELETE-ALL-DATA (refused on a cluster node and on the witness)
Recover a locked or forgotten admin./taranac unlock / ./taranac reset-password
Run a single protocol onlyOpen just that protocol’s inbound ports; the daemons still all start
  • A service shows unhealthy in ./taranac ps — read its logs: ./taranac logs --tail=100 <service>.
  • Browser still warns after Step 5 — confirm config/tls/tls.crt is the full chain and its SAN matches your domain, then ./taranac restart edge. The edge logs every address the certificate misses: ./taranac logs edge.
  • A new TARANAC_EXTRA_ADDRESSES value had no effect — you restarted the edge instead of recreating it. ./taranac up -d edge.
  • A collector or DMZ portal refuses to connect while a browser only warns — the address is in the certificate as a DNS: entry and needs to be an IP: one. See Answering on more than one address.
  • A TLS client or an EST device refuses the certificate after the node’s address changed — TARANAC_NODE_IPS still names the old address. Edit it, then ./taranac up -d; the generated certificates follow.
  • ./taranac logs <service> says no <service> container on this host — that service was never created here (a typo, or a host whose stack has not been started). It lists the containers that do exist; with none, start the stack with ./taranac up -d.
  • docker ps needs sudo — you didn’t start a new login shell after bootstrap.sh; log out/in or run newgrp docker.
  • The installer refuses before starting: Docker too old, Podman, kernel IPv6 off, or a dual-stack network Docker would not create — nothing was changed. Follow the fix it prints, then run it again. See IPv6 → What the host needs.
  • IPv6 devices are logged or refused with the Docker gateway’s address — Docker’s ip6tables is off. Run ./taranac diagnose; its ipv6.txt shows Docker’s version, whether its ip6tables is on, the daemons’ IPv6 listeners and IPv6 DNAT per published port, and accept_ra. See IPv6.