Self-hosting
Run your own Kutup in minutes
Kutup is self-hosted by design. A production deployment is a Docker Compose stack — backend, database, encrypted storage, and Nginx. Here’s the quick start; the full guide lives in the docs.
Prerequisites
- Docker 24+ and Docker Compose v2 (the docker compose command).
- A Linux server with at least 1 GB of RAM.
- A domain name — required for HTTPS and for federation to work correctly.
1. Clone and configure
Clone the repo and create your environment file:
git clone https://github.com/kutupbt/kutup.git
cd kutup
cp .env.example .envEdit .env and fill in every value with strong secrets:
# PostgreSQL — use a strong random password
POSTGRES_PASSWORD=<strong-random-password>
# JWT secret — generate with: openssl rand -hex 64
JWT_SECRET=<64-byte-hex-string>
# SeaweedFS S3 credentials — Compose injects them into every service
S3_ACCESS_KEY=kutup
S3_SECRET_KEY=<strong-random-secret>
S3_BUCKET=kutup-files
# Stable account/federation domain — set before creating accounts
CHAT_SERVER_NAME=kutup.example.com
# Public URL — used to build federation invite links
SERVER_URL=https://kutup.example.com
# Break-glass admin bootstrap — a single email:username:password triple.
# This account can never be demoted, disabled, or deleted; promote any
# further admins from inside the app.
ADMIN_ACCOUNT=admin@example.com:admin:<strong-admin-password>Compose injects the S3 credentials into SeaweedFS and the backend; there is no second credential file to edit.
2. Start the stack
Build and launch all services — Postgres, SeaweedFS, backend, frontend, and Nginx:
docker compose up -d --build --wait3. First login
Find the admin bootstrap confirmation in the logs, then open your domain and log in:
docker compose logs backend | grep -i "admin\|bootstrap"On first login you’ll generate your 24-word recovery phrase (write it down — it’s the only way to recover your account and is never sent to the server) and can optionally enable 2FA.
4. Add TLS
The bundled Nginx already requires TLS. Issue a certificate, place fullchain.pem and privkey.pem in nginx/certs/, and restart or reload Nginx:
certbot certonly --standalone -d kutup.example.com
cp /etc/letsencrypt/live/kutup.example.com/fullchain.pem nginx/certs/
cp /etc/letsencrypt/live/kutup.example.com/privkey.pem nginx/certs/
docker compose exec nginx nginx -s reloadOperating your instance
Backups
Dump PostgreSQL and archive the SeaweedFS data dirs. The file chunks are ciphertext only — a stolen backup is useless without user keys.
Updating
git pull then docker compose up -d --build. Database migrations run automatically on backend startup.
Reverse proxy
Already running Nginx or Caddy? Bind the stack to 127.0.0.1:8080 and proxy to it (disable request buffering for large uploads).
Hardening
Change every secret, expose only the public TLS edge, and generate JWT_SECRET with openssl rand -hex 64. Keep the backend unreachable except through Nginx and protect the break-glass admin.
Office editing works in the default Compose build without DocumentServer or manual installation. For durable SeaweedFS metadata, retention, quotas, federation, and backup/restore requirements, see the documentation ↗.
Already running Kutup?
List your server in the public instance directory so others can find it.