Docs
Self-hosting
Run FEATit on your own hardware with Docker Compose — the steps, the backups, the upgrades, and exactly what you do not get.
On featit.com
On featit.com you need none of this: we run the deployment for you. Getting started takes you from sign-up to your first items. The rest of this page is for running FEATit on your own servers.
On-premise
Coming: a guided server installation. Today you install FEATit with Docker Compose, as below.
This page is the on-premise path. It starts your deployment, and Getting started picks up from your first account.
You can run the whole of FEATit yourself: the same core product, the same schema, the same API and CLI, and the same desktop app pointed at your own infrastructure. There is no self-hosted edition and no reduced version.
This page summarises the run book, which has been executed end to end — including an upgrade from a build two migrations behind.
It starts from a checkout of the FEATit repository. Compose builds the images on your machine, and the first build takes several minutes.
What you do not get
Read this first. It is the part most self-hosting pages leave out.
| You do not get | Why |
|---|---|
| A downgrade path | Migrations run when the API starts. Once one has run, it is the schema, and the previous build does not know it. The way back from an upgrade is a restore from your backup. |
| Email before you set it up | Nobody but the administrator can sign up until mail is set up in the app, because sign-up sends a confirmation. It goes out through your own SMTP server or through Mailgun. |
| The admin panel | It is an internal operations surface and is not part of a self-hosted deployment. |
| Error reporting or metrics | Nothing reports errors anywhere. Your logs are docker compose logs. |
Before you start
- Docker with Compose v2, and about 4 GB of RAM free.
- A checkout of the FEATit repository.
- Two free ports, one for the app and one for storage. The defaults are 8080 and 8090.
Run it
cd code
cp .env.selfhost.example .env.selfhost
chmod 600 .env.selfhost
Open .env.selfhost and fill in the four secrets: the token signing secret, the database and storage passwords, and the deployment key that seals every secret you later enter in the app, such as your mail server’s password. Each has the command that generates it written next to it. Generate them — do not choose them, and never copy a value printed in any document.
docker compose --env-file .env.selfhost -f docker-compose.selfhost.yml up -d --build
The services start in order, each waiting for the one below it to be healthy, so when the command returns the deployment is serving. Open http://localhost:8080.
If a secret is missing, the stack refuses to start and names the variable. No secret has a default, so there is no way for it to come up quietly with a development password.
Your first account and administrator
Nobody can sign up until the deployment has an administrator, and it gets one in one of three ways, none of which needs mail:
- In a browser, within 15 minutes of starting the stack: open
http://localhost:8080, and you land on Set up this deployment. - From an agent on the host, with the FEATit CLI’s
featit bootstrap, which creates your account and prints a link where you set your password. - With a one-time setup token, minted inside the API container:
docker compose --env-file .env.selfhost -f docker-compose.selfhost.yml exec backend \
node backend/dist/src/setup/mint-setup-token.js
Sign in as the administrator, open Settings → Email, and choose SMTP server — any mail server, including one on your own network with no route to the internet — or Mailgun. For SMTP you enter the host, the port, how the connection is protected (STARTTLS, TLS, or none for an internal relay without a login), a username and password if the server asks for one, and the from address. The password is stored encrypted and never shown again. Send yourself a test message from the same screen.
The server’s certificate is always checked. If it comes from your organisation’s own certificate authority, the run book shows how to give the API that authority.
Once mail works, people can register, and a forgotten password can be reset.
Check it is serving:
curl http://localhost:8080/api/health
curl http://localhost:8080/api/deployment/capabilities
Behind a TLS proxy
The deployment is one origin: the web service serves the app and proxies /api/ to the API, so terminate TLS in front of it and forward everything there. Storage is the exception and needs a hostname of its own, because its download links are signed for a host. Set FEATIT_PUBLIC_URL to the app’s address and FEATIT_STORAGE_PUBLIC_URL to storage’s.
Backups
Two things are irreplaceable: the database and the attachments.
cd code
DC="docker compose --env-file .env.selfhost -f docker-compose.selfhost.yml"
# The database, as a custom-format dump.
$DC exec -T db sh -c 'pg_dump --username="$POSTGRES_USER" --dbname="$POSTGRES_DB" --format=custom' \
> featit-db.dump
# The attachments: stop the store for the copy, so it is one consistent state.
$DC stop storage
$DC cp storage:/data ./featit-objects
$DC start storage
Keep the backup somewhere the deployment cannot reach, and keep .env.selfhost with it — the signing secret and the deployment key in it cannot be recovered. Treat that file like a private key.
Upgrading
cd code
git pull
docker compose --env-file .env.selfhost -f docker-compose.selfhost.yml up -d --build
That is the whole procedure: migrations run when the API starts. Take a backup first — there is no downgrade, and the backup is what makes that survivable.
A deployment set up before 28 September 2026 kept its attachments in MinIO. The current version stores them in SeaweedFS, in a new volume, and leaves the old one untouched. The command that copies attachments across is not published yet, so if your deployment already holds attachments, stay on the version you run until it is.