Deployment Guide

Production deployment options for Snip.

Docker Compose (Recommended)

docker-compose.yml
version: "3.8"
services:
  snip:
    image: vesper/snip:latest
    container_name: snip
    ports:
      - "53524:53524"
    volumes:
      - snip-data:/app/data
    environment:
      - SNIP_BASE_URL=https://snip.yourdomain.com
      - SNIP_JWT_SECRET=your-random-32-byte-secret-here
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:53524/healthz"]
      interval: 30s
      timeout: 3s
      retries: 3

volumes:
  snip-data:
bash
docker-compose up -d
docker-compose logs -f snip

Systemd Service

The install script auto-creates this. Manual setup:

/etc/systemd/system/snip.service
[Unit]
Description=Snip - code & file sharing
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/snip
Environment=SNIP_PORT=53524
Environment=SNIP_DB_PATH=/var/lib/snip/snip.db
Environment=SNIP_BASE_URL=https://snip.yourdomain.com
Restart=on-failure
RestartSec=5
User=snip
Group=snip
WorkingDirectory=/var/lib/snip

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload
sudo systemctl enable snip
sudo systemctl start snip
sudo systemctl status snip
sudo journalctl -u snip -f

Kubernetes

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: snip
spec:
  replicas: 1
  selector:
    matchLabels:
      app: snip
  template:
    metadata:
      labels:
        app: snip
    spec:
      containers:
      - name: snip
        image: vesper/snip:latest
        ports:
        - containerPort: 53524
        livenessProbe:
          httpGet:
            path: /healthz
            port: 53524
        readinessProbe:
          httpGet:
            path: /healthz
            port: 53524
        volumeMounts:
        - name: data
          mountPath: /app/data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: snip-data
---
apiVersion: v1
kind: Service
metadata:
  name: snip
spec:
  selector:
    app: snip
  ports:
  - port: 80
    targetPort: 53524
  type: ClusterIP

Nginx Reverse Proxy

nginx.conf
server {
    listen 80;
    server_name snip.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name snip.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/snip.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/snip.yourdomain.com/privkey.pem;

    # Security headers
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Max upload size
    client_max_body_size 10M;

    location / {
        proxy_pass http://127.0.0.1:53524;
        proxy_set_header Host $host;
        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;
    }
}

Caddy Reverse Proxy

Caddyfile
snip.yourdomain.com {
    reverse_proxy localhost:53524
    encode gzip
    header {
        X-Frame-Options "DENY"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "strict-origin-when-cross-origin"
    }
    client_max_body 10MB
}

Caddy automatically provisions and renews HTTPS certificates.

SSL/HTTPS Setup

For production, always use HTTPS. Options:

bash
# Install certbot
sudo apt install certbot python3-certbot-nginx

# Get certificate
sudo certbot --nginx -d snip.yourdomain.com

# Auto-renewal is set up automatically
sudo certbot renew --dry-run
Remember: Set SNIP_BASE_URL to your public HTTPS URL.