Kutup

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:

sh
git clone https://github.com/kutupbulut/kutup.git
cd kutup
cp .env.example .env

Edit .env and fill in every value with strong secrets:

.env
# 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:

sh
docker compose up -d --build

3. First login

Find the admin bootstrap confirmation in the logs, then open your domain and log in:

sh
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:

sh
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.