Skip to main content

Installation

This walks a fresh install from an empty host to a running app.

Prerequisites

RequirementMinimum
OSLinux x86-64 (Ubuntu, RHEL, SLES, Debian, Fedora)
Container engineDocker 20.10+ with Compose v2, or Podman 4.4+ with podman-compose
RAM8 GB (16 GB for Pro/Enterprise workloads)
Disk20 GB free
Privilegesroot / sudo (installs under /opt/factorythread)
NetworkOutbound 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
Pin the version

--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

  1. Engine — detects Docker/Podman (offers to install Podman if none).
  2. Bundle — downloads factory-thread-<version>.tar.gz and verifies its SHA-256 + ECDSA signature against the trust anchor in the binary. The download is cached; re-runs reuse it (--refetch forces a fresh pull).
  3. Images — extracts the bundle and loads the image tarballs as local localhost/factorythread-* tags. No registry, no docker login.
  4. Config — on a fresh box, generates .env.deploy with all secrets and sensible defaults, prompting only for optional capabilities (below). On a box that already has .env.deploy, this step is skipped.
  5. 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> (or https://<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

Do this immediately

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

FlagDefaultMeaning
--version <v>latestrelease to install (pin it for production)
--bundle <file>air-gapped local bundle
--refetchoffre-download even if cached
--out <dir>./bundleswhere the bundle is saved
--control <url>baked-inoverride the Control Service URL

Engine & flow

FlagDefaultMeaning
--install-engineoffinstall Podman + compose if none
--engine docker|podmanautoforce the engine
--planoffdo the safe phases, stop before deploy
--workdir <dir>/opt/factorythread/currentextract/deploy dir
--env-file <path>.env.deployconfig file
--yesoffnon-interactive (skip prompts)

Config (fresh install)

FlagMeaning
--hostname <h> / --frontend-url <url>this install's public URL
--auth0-domain / --auth0-audience / --auth0-redirectoverride defaulted Auth0 values
--upload-path / --upload-base-pathupload host / container paths
--localfile-path / --localfile-base-path / --localfile-foldersflat-file paths + folders
--tls self-signed|existing|none (+ --cert --key)TLS mode
--forceregenerate .env.deploy (⚠️ new secrets)

Disaster recovery

FlagMeaning
--secrets <file> + --password <pw>restore saved secrets so a rebuilt box decrypts its restored DB