Skip to content

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

bash
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in

2. Clone and configure

bash
git clone https://github.com/ItsAshn/kizuna.git
cd kizuna
cp .env.example .env

Edit .env — set at minimum:

VariableDescription
DOMAINYour domain name
JWT_SECRETRun openssl rand -hex 64

See Configuration for all available variables.

3. Prepare data directories

bash
mkdir -p data uploads
chown -R 1000:1000 data uploads

The container runs as UID 1000. data/ holds the SQLite database, uploads/ holds attachments and GIFs.

4. Configure firewall

bash
# UFW (Ubuntu/Debian)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 40000:40099/udp
sudo ufw enable
bash
# 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 --reload

Ports 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

bash
docker compose up -d

This 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

bash
docker compose ps
docker compose logs kizuna
curl -I https://your-domain.com/health

Visit 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

bash
# Pull latest image and recreate
docker compose pull && docker compose up -d

For safe updates with health-check verification and automatic rollback:

bash
./scripts/update.sh                 # latest version
./scripts/update.sh --pin v0.2.0   # pin to a specific release
./scripts/update.sh --no-verify     # skip health check

The 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

bash
# Edit docker-compose.yml and change the image tag:
#   image: ghcr.io/itsashn/kizuna:v0.2.0
# Then:
docker compose pull && docker compose up -d

Development builds

If you need to build the image locally (e.g. for testing changes):

bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build

This 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.yml

Backups

Your data lives in data/ and uploads/ directories on the host. Back them up however you prefer:

bash
tar -czf kizuna-backup.tar.gz data/ uploads/