Skip to content

Deployment

ComponentMinimumRecommended
CPU1 core2+ cores
RAM512MB1GB+
Storage5GB10GB+
Node.jsBunLatest
Terminal window
git clone https://github.com/knitly-app/knitly.git
cd knitly
# Edit environment values directly in docker-compose.yml, then:
docker compose up -d

Open http://localhost:3000 — the first user to sign up becomes admin.

The shipped docker-compose.yml runs a single service and stores all state in a named volume mounted at /data. Set your production values in the environment block:

services:
knitly:
build: .
ports:
- "3000:3000"
volumes:
- knitly-data:/data
environment:
- NODE_ENV=production
- PORT=3000
- BASE_URL=https://your-domain.com
- ALLOWED_ORIGINS=https://your-domain.com
- DATABASE_PATH=/data/knitly.db
- LOCAL_UPLOAD_DIR=/data/uploads
- USE_LOCAL_STORAGE=true
restart: unless-stopped
volumes:
knitly-data:

The image builds the frontend, runs on port 3000, and ships a healthcheck against /api/health. Both the SQLite database and uploads live under /data, so backing up that one volume captures everything.

Terminal window
# Copy the database out of the running container
docker compose cp knitly:/data/knitly.db ./knitly.db.$(date +%Y%m%d)
Terminal window
# Ubuntu/Debian
sudo apt update
sudo apt install bun sqlite3 ffmpeg git
# Arch Linux
sudo pacman -Sy bun sqlite ffmpeg git
Terminal window
# Clone repository
git clone https://github.com/knitly-app/knitly.git
cd knitly
# Install dependencies
bun install
# Build frontend
bun run build
# Create environment file
cp deploy/env.production.example server/.env.production
# Edit with your values
# Create data directories
mkdir -p uploads

The repository ships a unit at deploy/knitly.service (and deploy/knitly.openrc for OpenRC systems). A minimal unit looks like this:

[Unit]
Description=Knitly Social Network
After=network.target
[Service]
Type=simple
User=knitly
WorkingDirectory=/opt/knitly
ExecStart=/usr/bin/bun --cwd /opt/knitly run start
Restart=always
RestartSec=10
Environment=NODE_ENV=production
Environment=BASE_URL=https://your-domain.com
Environment=ALLOWED_ORIGINS=https://your-domain.com
[Install]
WantedBy=multi-user.target

Configuration can also live in server/.env.production instead of Environment= lines.

Terminal window
# Create user
sudo useradd -r -s /bin/false knitly
# Copy the built app (repo root, no app/ subdirectory)
sudo cp -r /path/to/knitly /opt/knitly
sudo chown -R knitly:knitly /opt/knitly
# Install and enable the service
sudo cp /opt/knitly/deploy/knitly.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable knitly
sudo systemctl start knitly
sudo systemctl status knitly

Create /etc/caddy/Caddyfile:

knitly.example.com {
reverse_proxy localhost:3000
# Security headers (optional, Knitly adds its own)
header {
X-Frame-Options "DENY"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
}
# Rate limiting (optional, Knitly handles this)
}

Install Caddy:

Terminal window
# Ubuntu/Debian
sudo apt install caddy
# Arch Linux
sudo pacman -S caddy

Create /etc/nginx/sites-available/knitly:

server {
listen 80;
server_name knitly.example.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Enable site:

Terminal window
sudo ln -s /etc/nginx/sites-available/knitly /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
  1. Connect your GitHub repository
  2. Set build command: bun install && bun run build
  3. Set start command: bun run start
  4. Add environment variables
  5. Set instance size: $12/mo (2GB RAM recommended)
  1. Create new project
  2. Connect GitHub repository
  3. Add environment variables
  4. Set port to 3000
  5. Deploy

Caddy automatically obtains and renews Let’s Encrypt certificates:

knitly.example.com {
reverse_proxy localhost:3000
}
server {
listen 443 ssl;
server_name knitly.example.com;
ssl_certificate /etc/letsencrypt/live/knitly.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/knitly.example.com/privkey.pem;
# Strong SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:3000;
# ... proxy settings
}
}
Terminal window
curl https://your-domain.com/api/health

Docker:

Terminal window
docker compose logs -f

systemd:

Terminal window
journalctl -u knitly -f

Monitor with:

  • CPU usage: top or htop
  • Memory: free -h
  • Disk: df -h
  • Network: nethogs

Knitly is designed for single-instance deployment. For larger deployments:

  1. Use a reverse proxy/load balancer
  2. Multiple instances behind load balancer (sticky sessions required)
  3. External database (PostgreSQL/MySQL) - not currently supported
  1. Upgrade server resources
  2. Enable S3 storage for media
  3. Use CDN for static assets
  • HTTPS enabled (Let’s Encrypt)
  • Firewall configured (only ports 80, 443, 22 open)
  • Environment variables set
  • Admin account created
  • Regular backups configured
  • Security updates enabled
  • Rate limiting active
  • Security headers configured
Terminal window
# Find process
lsof -i :3000
# Kill process
kill -9 <PID>
Terminal window
# Stop server
sudo systemctl stop knitly
# Kill any remaining processes
kill -9 $(pgrep -f knitly)
# Restart
sudo systemctl start knitly
Terminal window
# Fix permissions
sudo chown -R knitly:knitly /opt/knitly
sudo chmod -R 755 /opt/knitly
Terminal window
# Check journal
journalctl -u knitly -n 50
# Check log directory
ls -la /opt/knitly/server/logs/
Terminal window
# Pull latest changes
cd /opt/knitly
git pull origin main
# Install dependencies
bun install
# Rebuild
bun run build
# Restart
sudo systemctl restart knitly
Terminal window
# Backup
cp knitly.db knitly.db.backup.$(date +%Y%m%d)
# Optimize
sqlite3 knitly.db "VACUUM;"
Terminal window
# Remove old uploads (manual review recommended)
find uploads -type f -mtime +365 -delete