Install
Running TaifaSupport on an Institution's own hardware, under its own domain, with its own data.
For an Institution running TaifaSupport on its own hardware, under its own domain, with its own data. No part of this install has to reach our infrastructure, and with the AI profile enabled no part of it has to reach the internet at all.
Everything here assumes EDITION=self_hosted, which is the default.
What you need
Hardware
| Use | CPU | RAM | Disk |
|---|---|---|---|
| Evaluation, one site, a handful of agents | 2 cores | 4 GB | 20 GB |
| Production, one busy portal, 10 to 30 agents | 4 cores | 8 GB | 100 GB SSD |
Production plus local AI (gemma2:2b) | 4 cores | 16 GB | 120 GB SSD |
Production plus local AI (gemma2:9b) | 8 cores | 32 GB, or 16 GB with an 8 GB GPU | 150 GB SSD |
Disk is dominated by page_views, which is the highest-volume table and grows
with traffic rather than with agents. A portal doing 50,000 pageviews a day
writes roughly 1 GB a month before indexes. Plan the disk against traffic, not
against headcount.
Software
- A Linux host. Ubuntu 22.04 or 24.04 LTS is what we test on.
- Docker Engine 24 or newer and the Compose plugin.
- A DNS name pointing at the host, if anything other than you is going to reach it.
Nothing else. PostgreSQL, Redis and nginx all come in the stack unless you choose to supply your own.
Install
Three values must change before this is used for anything real:
Also set CORS_ORIGINS to the same host as PUBLIC_URL, and
COOKIE_SECURE=true if you are serving over https, which you should be.
Bring it up:
make migrate is a separate step on purpose. The stack starting and the schema
changing are different events, and a deployment that silently migrates on boot
is one that can corrupt a database because a container restarted at the wrong
moment.
Open PUBLIC_URL. Because this is a self-hosted install with no Institution
yet, you land on the first-run wizard.
Try it with demo data first
If you would rather see the product full before pointing it at a real portal:
This creates one demo Institution with 40 visitors, live sessions,
conversations, tickets and a knowledge base. It is idempotent, and it only ever
touches the Institution whose slug is demo-lands, so it is safe to run on an
install that already has a real tenant.
The port map
Every service binds 127.0.0.1:<port>. nginx is the only thing that answers
from outside the host. This is not negotiable and it is worth re-checking after
any compose edit.
The check worth running after every compose edit:
Anything other than nginx printing 0.0.0.0 is a bug in the compose file.
Production hardening
A production install adds three things to the above.
Firewall
Everything in the stack already binds loopback, so this is defence in depth rather than the only thing standing between PostgreSQL and the internet. A misconfigured compose file has happened to everyone, and this is the layer that survives it.
A non-login system user
TLS
The production nginx terminates TLS itself. Issue the certificate before the
first up, because nginx will not start if the paths do not exist.
You do not edit the vhost. It is rendered from a template, and the hostname
comes from PUBLIC_ORIGIN in your .env:
That fills the placeholder into infra/nginx/generated/, then runs
nginx -t on the result in a throwaway container, so a bad vhost is caught
before anything is asked to serve it. make deploy does the same thing on
your behalf.
Renewal, once nginx is up, uses the webroot rather than standalone so it does not need port 80 to itself. Run this once to move an existing certificate over, and the deploy hook keeps nginx picking up each new one:
Starting production
docker-compose.prod.yml is standalone, not an override. Overrides merge in
ways that are hard to predict from reading either half, and this is the one
file where a mistake exposes a database to the internet.
Verify before telling anyone it is live:
Routine deployment
From the checkout, on the server:
which is:
The order is the point:
- Build first. A syntax error fails before anything is taken down.
- Migrate before restarting. New code must never meet an old schema. Migrations are additive within a minor release, so the old containers keep serving correctly while this runs.
- Prune last. If the migration step fails, the previous image is still on
the host and a rollback is one
git checkoutaway.
Take a dump before every deploy, without exception. See backups and upgrades.
Next
- Using infrastructure you already have: an existing PostgreSQL, Redis or S3-compatible storage.
- Private AI: running Gemma on the Institution's own hardware, with honest numbers about what it needs.
- Backups and upgrades.