How to Deploy Taiga on a VPS
Updated Sep 2026
verified on Ubuntu 26.04 · Sep 2026Self-host Taiga on a VPS with the official taiga-docker stack — generated secrets, a non-interactive admin account, the gateway kept on loopback, HTTPS and WebSockets through Caddy.
- A VPS with at least 2 GB RAM
- A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
- A domain or subdomain you can point at the server
- Docker Engine + Compose installed (see the base guide below)
- git
What Taiga is
Taiga is an open-source agile project management platform built around Scrum and Kanban: backlogs, sprints, epics, user stories, issue tracking and a wiki. The backend is Python (Django), the frontend AngularJS, the database PostgreSQL. It is licensed MPL-2.0 and rated 3 / 5 to deploy.
Self-hosting is the same open-source code as Taiga's hosted product, with no project, member or storage caps and no feature gate. What you pay for, if anything, is the hosted service or an optional support contract.
If your team doesn't work in sprints, a general-purpose tool may fit better — see Deploy Plane on a VPS or Deploy OpenProject on a VPS.
Server sizing
taiga-docker runs nine containers: the backend, an async worker, the
frontend, the events (WebSocket) server, the protected-attachments service,
two RabbitMQ instances, PostgreSQL and an nginx gateway.
- Minimum: 2 GB RAM — the catalog's floor for the stack.
- Measured: on a GCP e2-standard-2 running Ubuntu 26.04 with Docker 29.8.1, the idle stack used ~722 MB of RAM and ~2.9 GB of disk for images and data (September 2026).
The two RabbitMQ instances are a large share of the idle memory. On a 2 GB box that leaves room for Caddy and the operating system, but not much else; move to 4 GB if the server also hosts other apps.
Prepare the server
This guide assumes Docker Engine and the Compose plugin are installed, along
with a non-root deploy user and a ufw firewall. If not, work through
Docker & Compose on Ubuntu first.
sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw --force enable
sudo apt-get install -y git
Paid link — we earn a commission if you shop through it.
Install Taiga (taiga-docker)
Clone upstream's taiga-docker
repository on its stable branch:
cd ~
[ -d taiga ] || git clone https://github.com/taigaio/taiga-docker.git --branch stable --depth=1 taiga
cd ~/taiga && git log -1 --format='%h %cd' --date=short
A heads-up before you go further: upstream's compose file pins
postgres:12.3, rabbitmq:3.8-management-alpine and
nginx:1.19-alpine — all past end of life — and the Taiga images
themselves on latest. This guide keeps upstream's file as published, because
that is what Taiga tests; moving to newer infrastructure images is a change to
research and test on your own before a production deploy.
Configure .env
All basic settings live in .env, and upstream says to change the security
ones. Four groups:
- URLs:
TAIGA_SCHEME=https,TAIGA_DOMAINyour domain, andWEBSOCKETS_SCHEME=wss— upstream's subdomain example. SECRET_KEYships as"taiga-secret-key", used for cryptographic signing.- Passwords:
POSTGRES_PASSWORD,RABBITMQ_PASSandRABBITMQ_ERLANG_COOKIEship astaiga,taigaandsecret-erlang-cookie. - Telemetry:
ENABLE_TELEMETRY=Trueby default sends anonymous usage data; set it toFalseto opt out.
cd ~/taiga
if grep -q '^SECRET_KEY="taiga-secret-key"' .env; then
sed -i \
-e "s|^SECRET_KEY=.*|SECRET_KEY=\"$(openssl rand -hex 32)\"|" \
-e "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$(openssl rand -hex 16)|" \
-e "s|^RABBITMQ_PASS=.*|RABBITMQ_PASS=$(openssl rand -hex 16)|" \
-e "s|^RABBITMQ_ERLANG_COOKIE=.*|RABBITMQ_ERLANG_COOKIE=$(openssl rand -hex 16)|" \
.env
fi
sed -i \
-e 's|^TAIGA_SCHEME=.*|TAIGA_SCHEME=https|' \
-e 's|^TAIGA_DOMAIN=.*|TAIGA_DOMAIN=taiga.example.com|' \
-e 's|^WEBSOCKETS_SCHEME=.*|WEBSOCKETS_SCHEME=wss|' \
-e 's|^ENABLE_TELEMETRY=.*|ENABLE_TELEMETRY=False|' \
.env
chmod 600 .env
grep -E '^(TAIGA_SCHEME|TAIGA_DOMAIN|WEBSOCKETS_SCHEME|ENABLE_TELEMETRY)=' .env
Replace taiga.example.com with your domain. Set the passwords before the
first start: Postgres and RabbitMQ only read them when their data volumes are
first created.
The gateway publishes port 9000 on every interface. Since Caddy will sit in front of it, bind it to the loopback interface instead:
cd ~/taiga
sed -i 's|- "9000:80"|- "127.0.0.1:9000:80"|' docker-compose.yml
grep -n '9000:80' docker-compose.yml
Start it
launch-taiga.sh is a one-line wrapper around docker compose up -d:
cd ~/taiga
./launch-taiga.sh
timeout 600 bash -c 'until curl -fs http://127.0.0.1:9000/api/v1/ >/dev/null; do sleep 5; done'
docker compose ps --format '{{.Service}}\t{{.Status}}'
The backend applies its database migrations on the first start, so the API can take a minute or two to answer.
Create the admin account
Upstream creates the first admin with Django's createsuperuser through the
taiga-manage service. The ./taiga-manage.sh createsuperuser wrapper asks for
the details interactively; Django's --noinput mode takes them from arguments
and DJANGO_SUPERUSER_PASSWORD instead, which suits a script. The password is
generated and kept in a file only you can read:
cd ~/taiga
if [ ! -f .admin-password ]; then
openssl rand -hex 12 > .admin-password && chmod 600 .admin-password
docker compose -f docker-compose.yml -f docker-compose-inits.yml run --rm \
-e DJANGO_SUPERUSER_PASSWORD="$(cat .admin-password)" \
taiga-manage createsuperuser --noinput --username admin --email admin@example.com
fi
Log in as admin with the password in ~/taiga/.admin-password, then change
it in the profile settings and delete the file.
HTTPS + domain
Point an A record for taiga.example.com at the server's public IP, wait
for it to resolve, then terminate TLS with Caddy:
taiga.example.com {
reverse_proxy 127.0.0.1:9000
}
Upstream's nginx example needs a separate location /events block with
Upgrade headers and week-long timeouts, because the events server holds a
WebSocket open for real-time board updates. Caddy passes WebSocket upgrades
through without extra configuration, so the single block covers both. If you use nginx, copy upstream's configuration exactly.
TAIGA_SCHEME=https also matters for the Django admin at /admin/: its
session cookies are marked secure, so it only works over HTTPS.
Securing it
- Registration: public sign-up is disabled by default. Keep it that way
and invite people from each project. Turning it on means setting
PUBLIC_REGISTER_ENABLEDin two places —"True"for the backend,"true"for the frontend — and GitHub/GitLab login buttons only appear when it is on. - Email: by default
EMAIL_BACKEND=console, so invitation and password-reset emails are printed to the backend log, not sent. SetEMAIL_BACKEND=smtpand theEMAIL_*variables in.env, then run./launch-taiga.shagain. - Attachments are served through the protected service with short-lived
tokens (
ATTACHMENTS_MAX_AGE, 360 seconds by default).
Backups
Two things hold your data: the PostgreSQL database and the media volume with uploaded attachments.
cd ~/taiga
mkdir -p ~/taiga-backups
set -o pipefail
docker compose exec -T taiga-db sh -c 'pg_dump -U "$POSTGRES_USER" taiga' | gzip > ~/taiga-backups/taiga-db-$(date +%F).sql.gz
docker run --rm -v taiga_taiga-media-data:/data -v ~/taiga-backups:/backup alpine \
tar czf /backup/taiga-media-$(date +%F).tar.gz -C /data .
ls -lh ~/taiga-backups
The volume name is prefixed with the project directory (taiga_); confirm it
with docker volume ls. Copy the archives and .env off the server —
without SECRET_KEY, restored sessions and signed links stop working.
Upgrades
The stable branch's compose file tracks latest Taiga images, so an upgrade is
a pull and a restart. Back up first:
cd ~/taiga
git checkout -- docker-compose.yml
git pull --ff-only
sed -i 's|- "9000:80"|- "127.0.0.1:9000:80"|' docker-compose.yml
docker compose pull
./launch-taiga.sh
docker compose ps --format '{{.Service}}\t{{.Status}}'
The loopback edit to docker-compose.yml is undone before git pull (so a
changed upstream file never blocks the pull) and re-applied after it.
Migrations run when the backend starts.
Troubleshooting
The page loads but boards don't update live. The events WebSocket isn't
connecting. Check that WEBSOCKETS_SCHEME=wss matches the HTTPS site and that
nothing between the browser and Caddy strips upgrade headers.
Login or the Django admin fails over HTTP. Taiga is configured for HTTPS by
default. Use the https:// URL; upstream documents SESSION_COOKIE_SECURE and
CSRF_COOKIE_SECURE overrides only for deliberate plain-HTTP setups.
Emails never arrive. EMAIL_BACKEND is still console. Read
docker compose logs taiga-back to see the messages that would have been sent.
The backend can't connect to Postgres or RabbitMQ. The passwords in .env
were changed after the volumes were created. Put the old values back, or remove
the volumes if they hold nothing you need.
Verification + next steps
You're done when you can: load https://taiga.example.com over a valid
certificate, log in as the admin you created, create a Scrum project, move a
user story across the board and see the change appear in a second browser
without refreshing, and find last night's dump off the server.
From there: configure SMTP, invite your team, and look at the Jira, Trello and GitHub importers in upstream's README if you're migrating. For hosting options, see Best VPS for Docker.