Installation
This walks a fresh install from an empty host to a running app.
Prerequisites
| Requirement | Minimum |
|---|---|
| OS | Linux x86-64 (Ubuntu, RHEL, SLES, Debian, Fedora) |
| Container engine | Docker 20.10+ with Compose v2, or Podman 4.4+ with podman-compose |
| RAM | 8 GB (16 GB for Pro/Enterprise workloads) |
| Disk | 20 GB free |
| Privileges | root / sudo (installs under /opt/factorythread) |
| Network | Outbound HTTPS to control.factorythread.com, or an offline bundle |
Everything installs under /opt/factorythread (override with FT_INSTALL_ROOT):
/opt/factorythread/
├── current/ # extracted bundle: compose + config + digests
│ ├── docker-compose.deploy.yml
│ └── .env.deploy # your config + generated secrets
├── bundles/ # downloaded, verified release bundles
├── backups/ # ftctl backup output
└── .installed-version # the deployed version marker
Step 1 — Get the ftctl binary
curl -fsSL https://control.factorythread.com/ftctl -o ftctl
chmod +x ftctl
./ftctl version # prints e.g. 2026.09.04.7
Step 2 — Preflight the host
Checks distro, engine, Compose provider and resources without changing anything:
sudo ./ftctl preflight
Fix anything it flags (usually a missing engine or Compose v2) before installing.
Step 3 — (optional) Install a container engine
If you have no Docker or Podman, ftctl can install Podman +
podman-compose:
sudo ./ftctl engine
You can skip this — install also offers to do it interactively when it
finds no engine. If you already run Docker, do nothing here.
Step 4 — Install
One command does everything: fetch → verify → load images → configure → deploy.
sudo ./ftctl install --version 2026.09.04.7
--version latest (or omitting it) resolves the newest release via the Control
Service — fine for a lab, but a pinned version can't shift under you
mid-install. Pin it for production.
The five phases
- Engine — detects Docker/Podman (offers to install Podman if none).
- Bundle — downloads
factory-thread-<version>.tar.gzand verifies its SHA-256 + ECDSA signature against the trust anchor in the binary. The download is cached; re-runs reuse it (--refetchforces a fresh pull). - Images — extracts the bundle and loads the image tarballs as local
localhost/factorythread-*tags. No registry, nodocker login. - Config — on a fresh box, generates
.env.deploywith all secrets and sensible defaults, prompting only for optional capabilities (below). On a box that already has.env.deploy, this step is skipped. - Deploy — brings the stack up. The migrate step runs migrations and provisions the runtime DB role, then the app starts.
The interactive prompts
Endpoints (Auth0, Control Service, frontend URL) are defaulted — no prompts. You're only asked, yes/no, about optional capabilities. Answer No to keep defaults; Yes walks each key, and pressing Enter keeps the current value:
Install Podman now? [Y/n] ← only if no engine
Configure file storage — uploads / local files? [y/N]
Configure worker memory? [y/N]
Enable the FactoryThread Assistant (AI)? [y/N]
Configure Grafana / Prometheus monitoring? [y/N]
A plain install can be all defaults — no required input. Each capability is explained in Configuration.
Non-interactive install
For scripted installs, skip every prompt:
sudo ./ftctl install --version 2026.09.04.7 --yes --hostname factory.acme.com
Step 5 — Verify
sudo ./ftctl status
Shows the installed version, per-service health and licence state. Then:
- App:
http://<host>(orhttps://<host>with TLS) - Backend health:
curl http://localhost:3000/health - Logs:
sudo ./ftctl logs backend -f
Step 6 — Install your licence
A fresh install is Unlicensed until you activate your plan — see Licensing:
sudo ./ftctl license install --jws "$(cat entitlement.jws)"
sudo ./ftctl license status
Step 7 — Back up your secrets
Secrets were generated on this box and never left it — including
ENCRYPTION_KEY, which decrypts your stored connection credentials. Without
it, a restored database is useless. Export and store them off the box now — see
Disaster Recovery.
sudo ./ftctl secrets export --password '<passphrase>' --file factorythread-secrets.enc
Install flags
Bundle source
| Flag | Default | Meaning |
|---|---|---|
--version <v> | latest | release to install (pin it for production) |
--bundle <file> | — | air-gapped local bundle |
--refetch | off | re-download even if cached |
--out <dir> | ./bundles | where the bundle is saved |
--control <url> | baked-in | override the Control Service URL |
Engine & flow
| Flag | Default | Meaning |
|---|---|---|
--install-engine | off | install Podman + compose if none |
--engine docker|podman | auto | force the engine |
--plan | off | do the safe phases, stop before deploy |
--workdir <dir> | /opt/factorythread/current | extract/deploy dir |
--env-file <path> | .env.deploy | config file |
--yes | off | non-interactive (skip prompts) |
Config (fresh install)
| Flag | Meaning |
|---|---|
--hostname <h> / --frontend-url <url> | this install's public URL |
--auth0-domain / --auth0-audience / --auth0-redirect | override defaulted Auth0 values |
--upload-path / --upload-base-path | upload host / container paths |
--localfile-path / --localfile-base-path / --localfile-folders | flat-file paths + folders |
--tls self-signed|existing|none (+ --cert --key) | TLS mode |
--force | regenerate .env.deploy (⚠️ new secrets) |
Disaster recovery
| Flag | Meaning |
|---|---|
--secrets <file> + --password <pw> | restore saved secrets so a rebuilt box decrypts its restored DB |