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/kutupbulut/kutup.git
cd kutup
cp .env.example .env Edit .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 — must match seaweedfs-s3.json
S3_ACCESS_KEY=kutup
S3_SECRET_KEY=<strong-random-secret>
S3_BUCKET=kutup-files
# 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> The S3 secret in seaweedfs-s3.json must match S3_SECRET_KEY.
2. Start the stack
Build and launch all services — Postgres, SeaweedFS, backend, frontend, and Nginx:
docker compose up -d --build 3. 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 listens on port 80. Issue a certificate with Certbot, drop fullchain.pem and privkey.pem into nginx/certs/, add a 443 server block, and reload:
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 reload Operating 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 all .env defaults, expose only 80/443, and generate JWT_SECRET with openssl rand -hex 64. The bootstrap account is a protected break-glass admin — give it a strong password.
For OnlyOffice setup, lifecycle/versioning config, federation, and the full reference, see the documentation ↗.
Already running Kutup?
List your server in the public instance directory so others can find it.