Self-Hosting
Deploy your own Kizuna server with Docker. Pre-built multi-arch images are pulled from ghcr.io/itsashn/kizuna — no build tools or compilation needed on your server.
Prerequisites
- A server running Linux (amd64 or arm64)
- A domain name with an A record pointing to your server's IP
- Docker and Docker Compose v2 installed
- Ports 80, 443 (TCP) and 40000-40099 (UDP) reachable
Setup
1. Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in2. Clone and configure
git clone https://github.com/ItsAshn/kizuna.git
cd kizuna
cp .env.example .envEdit .env — set at minimum:
| Variable | Description |
|---|---|
DOMAIN | Your domain name |
JWT_SECRET | Run openssl rand -hex 64 |
See Configuration for all available variables.
3. Prepare data directories
mkdir -p data uploads
chown -R 1000:1000 data uploadsThe container runs as UID 1000. data/ holds the SQLite database, uploads/ holds attachments and GIFs.
4. Configure firewall
# UFW (Ubuntu/Debian)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 40000:40099/udp
sudo ufw enable# Firewalld (Fedora/RHEL)
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-port=40000-40099/udp
sudo firewall-cmd --reloadPorts 80/443 are for HTTPS via Caddy. UDP 40000-40099 are for WebRTC voice — Caddy can't proxy UDP, so these are exposed directly from the Kizuna container.
If your VPS has an external firewall (DigitalOcean, AWS, etc.), open these ports there too.
5. Launch
docker compose up -dThis pulls the pre-built image from ghcr.io/itsashn/kizuna:latest and starts everything. Caddy will automatically obtain a Let's Encrypt certificate on first run.
6. Verify
docker compose ps
docker compose logs kizuna
curl -I https://your-domain.com/healthVisit https://your-domain.com and create an account.
How it works
Internet
├── :443 (HTTPS) → Caddy → kizuna:5000 (HTTP + WebSocket)
└── :40000-40099 (UDP) → Kizuna (mediasoup WebRTC SFU)Caddy handles TLS termination and reverse-proxies to the Kizuna server on port 5000. WebRTC voice traffic bypasses Caddy entirely on UDP.
Updating
Automatic (default)
The compose file includes Watchtower, which polls for new images every hour and automatically updates the Kizuna container when a new release is available. No action needed — updates happen in the background.
To disable automatic updates, remove the watchtower service from docker-compose.yml.
Manual
# Pull latest image and recreate
docker compose pull && docker compose up -dFor safe updates with health-check verification and automatic rollback:
./scripts/update.sh # latest version
./scripts/update.sh --pin v0.2.0 # pin to a specific release
./scripts/update.sh --no-verify # skip health checkThe update script pulls the image, recreates the container, waits for /health to respond, and rolls back to the previous image on failure.
Pinning to a release
# Edit docker-compose.yml and change the image tag:
# image: ghcr.io/itsashn/kizuna:v0.2.0
# Then:
docker compose pull && docker compose up -dDevelopment builds
If you need to build the image locally (e.g. for testing changes):
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --buildThis requires the build tools (python3, make, g++) inside the Docker build stage — not on your host.
Directory layout
kizuna/
├── .env # Your configuration
├── data/ # SQLite database (persistent)
├── uploads/ # Attachments, GIFs (persistent)
├── Caddyfile # Reverse proxy config
└── docker-compose.ymlBackups
Your data lives in data/ and uploads/ directories on the host. Back them up however you prefer:
tar -czf kizuna-backup.tar.gz data/ uploads/