Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/egeuysall/ryva-archive/llms.txt

Use this file to discover all available pages before exploring further.

This guide covers deploying Ryva to production environments using Docker containers.

Overview

Ryva uses a containerized architecture with:
  • Backend: Go API in Alpine Linux container
  • Caddy: Reverse proxy with SSL, rate limiting, and security headers
  • GitHub Container Registry (GHCR): Pre-built production images

Architecture

┌─────────────┐
│   Internet  │
└──────┬──────┘


┌─────────────────┐
│  Caddy (Port 80/443)  │  
│  - SSL/TLS      │
│  - Rate Limiting│
│  - CORS Headers │
└──────┬──────────┘


┌─────────────────┐
│  Backend:8080   │
│  (Go API)       │
└──────┬──────────┘


┌─────────────────┐
│  PostgreSQL     │
│  (Supabase)     │
└─────────────────┘

Prerequisites

1

Server Requirements

  • OS: Ubuntu 22.04+ or similar Linux distribution
  • RAM: Minimum 1GB (2GB+ recommended)
  • CPU: 1 vCPU minimum (2+ recommended)
  • Disk: 10GB+ available space
  • Docker: 24.0+ installed
  • Docker Compose: 2.0+ installed
2

Domain Requirements

  • Domain name (e.g., ryva.dev)
  • DNS A record pointing to your server IP
  • Subdomain for API (e.g., api.ryva.dev)
3

External Services

  • Supabase project with PostgreSQL database
  • (Optional) Sentry account for error tracking
  • (Optional) Stripe account for payments

Production Setup

Step 1: Server Preparation

1

Install Docker

# Update system packages
sudo apt update && sudo apt upgrade -y

# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Add user to docker group
sudo usermod -aG docker $USER

# Log out and back in for group changes to take effect
2

Install Docker Compose

# Docker Compose is included with Docker by default
# Verify installation
docker compose version
3

Configure firewall

# Allow SSH, HTTP, and HTTPS
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

# Verify firewall status
sudo ufw status

Step 2: Clone Repository

# Clone the repository
git clone https://github.com/egeuysall/ryva.git
cd ryva

# Checkout the production branch (or specific version tag)
git checkout master
For production, always use tagged releases (e.g., git checkout v1.0.0) rather than branch heads.

Step 3: Environment Configuration

1

Create API environment file

# Create .env from example
cp apps/api/.env.example apps/api/.env

# Or use the make command
make setup-vps
2

Configure API environment variables

Edit apps/api/.env with production values:
nano apps/api/.env
Required Configuration:
# Application
GO_ENV=production
PORT=8080

# Supabase
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-production-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-production-service-role-key
DATABASE_URL=postgresql://postgres:[password]@db.your-project.supabase.co:5432/postgres

# JWT
JWT_SECRET=your-secure-random-secret-min-32-chars
JWT_EXPIRY=24h

# CORS
ALLOWED_ORIGINS=https://ryva.dev,https://www.ryva.dev

# Sentry (optional)
SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
SENTRY_ENVIRONMENT=production

# Email (Resend)
RESEND_API_KEY=re_your_api_key
RESEND_FROM_EMAIL=noreply@ryva.dev
RESEND_FROM_NAME=Ryva

# Stripe (optional)
STRIPE_SECRET_KEY=sk_live_your_key
STRIPE_WEBHOOK_SECRET=whsec_your_secret
Use strong, randomly generated secrets for JWT_SECRET in production:
openssl rand -base64 32
3

Configure Docker environment

Create environment file for Docker Compose:
nano .env
# GitHub Container Registry settings
GITHUB_REPOSITORY_OWNER=egeuysall
IMAGE_TAG=latest
CADDY_IMAGE_TAG=latest

Step 4: Configure Caddy

1

Update Caddyfile with your domain

Edit infra/caddy/Caddyfile:
nano infra/caddy/Caddyfile
Update the domain and email:
{
    email your-email@example.com  # For Let's Encrypt notifications
    grace_period 30s
    admin off
}

# Your API domain
api.yourdomain.com {
    # ... rest of configuration
}
2

Update CORS origins

In the same Caddyfile, update CORS headers:
Access-Control-Allow-Origin "https://yourdomain.com"

Step 5: Deploy with Docker Compose

1

Pull production images

# Pull latest images from GHCR
docker compose pull
2

Start services

# Start containers in detached mode
docker compose up -d

# Or use make command
make docker-up
3

Verify deployment

# Check container status
docker compose ps

# View logs
docker compose logs -f

# Check backend health
curl https://api.yourdomain.com/health

Local Docker Development

For testing Docker setup locally:
# Use local compose file that builds from source
docker compose -f docker-compose.local.yml up -d

# View logs
docker compose -f docker-compose.local.yml logs -f

# Stop services
docker compose -f docker-compose.local.yml down
Local development uses Caddyfile.local which works with localhost domains.

Docker Commands Reference

Container Management

# Start containers
make docker-up
# or
docker compose up -d

# Stop containers
make docker-down
# or
docker compose down

# Restart containers
docker compose restart

# View container status
docker compose ps

# View resource usage
docker stats

Logs and Debugging

# View all logs
make docker-logs
# or
docker compose logs -f

# View specific service logs
docker compose logs -f backend
docker compose logs -f caddy

# View last 100 lines
docker compose logs --tail=100 backend

# Execute command in running container
docker compose exec backend sh

Updates and Rebuilds

# Pull latest images
docker compose pull

# Rebuild and restart
docker compose up -d --build

# Build specific service
docker compose build backend

# Remove unused images
docker image prune -a

Multi-stage Docker Build

Ryva’s backend uses a multi-stage Dockerfile for optimal image size:
# Stage 1: Build
FROM golang:1.25-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o api ./cmd/server/main.go

# Stage 2: Runtime
FROM alpine:3.20
RUN apk add --no-cache ca-certificates curl
RUN adduser -D -s /bin/sh appuser
USER appuser
WORKDIR /app
COPY --from=builder /app/api .
CMD ["./api"]
Benefits:
  • Small final image (~20MB vs 300MB+)
  • No build tools in production
  • Runs as non-root user
  • Includes health check

Container Resource Limits

Production containers have resource limits configured:
services:
  backend:
    mem_limit: 512m    # Maximum 512MB RAM
    cpus: 0.5          # Maximum 50% of 1 CPU core
Adjust these limits based on your server resources and application needs.

Health Checks

Both services include health checks:

Backend Health Check

healthcheck:
  test: ['CMD', 'wget', '--quiet', '--tries=1', '-O', '-', 'http://localhost:8080/health']
  interval: 10s
  timeout: 5s
  retries: 3
  start_period: 10s

Caddy Health Check

Caddy monitors backend health and stops routing traffic to unhealthy instances:
reverse_proxy backend:8080 {
    health_uri /health
    health_interval 10s
    health_timeout 5s
    health_status 2xx
}

SSL/TLS Configuration

Caddy automatically obtains and renews SSL certificates from Let’s Encrypt:
1

Automatic SSL

Caddy handles SSL automatically when you use a domain name:
  • Obtains certificates on first request
  • Renews certificates automatically before expiry
  • Enforces HTTPS redirects
2

Verify SSL

# Test SSL certificate
curl -vI https://api.yourdomain.com

# Check certificate expiry
echo | openssl s_client -connect api.yourdomain.com:443 2>/dev/null | openssl x509 -noout -dates

Zero-Downtime Deployments

For production updates with minimal downtime:
1

Pull new images

docker compose pull
2

Restart with rolling update

# Stop and remove old containers
docker compose down

# Start new containers
docker compose up -d
3

Verify deployment

# Check all containers are healthy
docker compose ps

# Monitor logs during startup
docker compose logs -f backend

# Test API endpoint
curl https://api.yourdomain.com/health
For true zero-downtime deployments, consider using container orchestration platforms like Kubernetes or Docker Swarm.

Monitoring and Logs

Log Configuration

Logs are configured with rotation to prevent disk space issues:
logging:
  driver: json-file
  options:
    max-size: '10m'    # Maximum 10MB per log file
    max-file: '3'       # Keep 3 rotated files

Caddy Access Logs

Caddy logs are stored in the caddy_logs volume:
# View Caddy access logs
docker compose exec caddy cat /var/log/caddy/api-access.log

# Follow Caddy logs
docker compose exec caddy tail -f /var/log/caddy/api-access.log

Application Logs

# Backend application logs
docker compose logs -f backend

# Filter logs by time
docker compose logs --since 1h backend

# Save logs to file
docker compose logs backend > backend.log

Backup and Recovery

Volume Backup

# Backup Caddy data (SSL certs, etc.)
docker run --rm \
  -v ryva_caddy_data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/caddy-data-backup.tar.gz -C /data .

Database Backup

Since Ryva uses Supabase (managed PostgreSQL), backups are handled by Supabase:
  • Automatic daily backups
  • Point-in-time recovery
  • Manual backups via Supabase dashboard

Troubleshooting

# Check container logs
docker compose logs backend

# Check container status
docker compose ps

# Inspect container
docker inspect ryva-backend

# Common issues:
# - Missing .env file
# - Invalid environment variables
# - Port conflicts
# - Database connection issues
# Check Caddy logs
docker compose logs caddy

# Common issues:
# - DNS not pointing to server
# - Firewall blocking port 443
# - Domain not accessible from internet
# - Rate limits from Let's Encrypt

# Force certificate renewal
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
# Check resource usage
docker stats

# Adjust memory limits in docker-compose.yml
# backend:
#   mem_limit: 1g  # Increase to 1GB

# Restart containers
docker compose down && docker compose up -d
# Check backend logs for migration errors
docker compose logs backend | grep migration

# Run migrations manually
docker compose exec backend ./api migrate-up

# Verify DATABASE_URL is correct
docker compose exec backend env | grep DATABASE_URL

Security Best Practices

Environment Variables

  • Never commit .env files to git
  • Use strong random secrets (32+ characters)
  • Rotate secrets regularly
  • Use different secrets for each environment

Container Security

  • Run containers as non-root user
  • Keep images updated
  • Scan images for vulnerabilities
  • Use minimal base images (Alpine)

Network Security

  • Enable firewall (ufw)
  • Use HTTPS only
  • Configure CORS properly
  • Implement rate limiting

Monitoring

  • Monitor container health
  • Set up alerts for failures
  • Review logs regularly
  • Use Sentry for error tracking

Next Steps

Database Migrations

Manage database schema changes in production

Environment Variables

Complete environment configuration reference