Deployment Guide

This comprehensive guide covers deploying Varel applications to production with Caddy, PostgreSQL, and systemd.

Table of Contents


Architecture Overview

Production Stack

┌──────────────────────────────────────────┐
│  Internet (HTTPS, port 443)              │
└────────────────┬─────────────────────────┘
                 │
          ┌──────▼───────┐
          │    Caddy     │  ← Automatic HTTPS
          │              │    HTTP/2, HTTP/3
          │  Port 443    │    TLS termination
          └──────┬───────┘
                 │
                 │ HTTP (localhost)
                 │
       ┌─────────▼─────────┐
       │  Varel App        │  ← Your application
       │                   │    Compiled V binary
       │  Port 8080        │
       └─────────┬─────────┘
                 │
                 │
       ┌─────────▼─────────┐
       │  PostgreSQL       │  ← Database
       │                   │
       │  Port 5432        │
       └───────────────────┘

Why This Stack?

  • Caddy: Automatic HTTPS with Let's Encrypt (zero-config)
  • Varel: Single compiled binary (easy to deploy)
  • PostgreSQL: Reliable, feature-rich database
  • Systemd: Process management and auto-restart

Server Setup

Requirements

  • OS: Ubuntu 20.04+ or Debian 11+
  • RAM: 512MB minimum, 1GB+ recommended
  • Disk: 10GB minimum
  • Domain: A domain pointing to your server IP

Initial Server Setup

# Update system
sudo apt update
sudo apt upgrade -y

# Install essentials
sudo apt install -y build-essential git curl wget

# Create application user
sudo useradd -r -s /bin/bash -d /opt/varel varel
sudo mkdir -p /opt/varel
sudo chown varel:varel /opt/varel

Firewall Setup

# Install UFW (Uncomplicated Firewall)
sudo apt install -y ufw

# Allow SSH
sudo ufw allow 22/tcp

# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable firewall
sudo ufw enable
sudo ufw status

Database Setup

Install PostgreSQL

# Install PostgreSQL
sudo apt install -y postgresql postgresql-contrib

# Start and enable
sudo systemctl start postgresql
sudo systemctl enable postgresql

# Verify
sudo systemctl status postgresql

Create Database and User

# Switch to postgres user
sudo -u postgres psql

# Create database and user
CREATE DATABASE varel_production;
CREATE USER varel WITH PASSWORD 'secure_random_password_here';
GRANT ALL PRIVILEGES ON DATABASE varel_production TO varel;

# Exit psql
\q

Configure PostgreSQL

# Edit postgresql.conf
sudo nano /etc/postgresql/14/main/postgresql.conf

# Set these values:
max_connections = 100
shared_buffers = 256MB
effective_cache_size = 1GB
work_mem = 4MB

# Edit pg_hba.conf
sudo nano /etc/postgresql/14/main/pg_hba.conf

# Add local connection (if needed):
# local   all   varel   md5

# Restart PostgreSQL
sudo systemctl restart postgresql

Backup Configuration

# Create backup script
sudo nano /usr/local/bin/backup-database.sh
#!/bin/bash
# Backup script
BACKUP_DIR="/var/backups/postgresql"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p $BACKUP_DIR

# Backup database
sudo -u postgres pg_dump varel_production | gzip > $BACKUP_DIR/varel_production_$DATE.sql.gz

# Keep only last 7 days
find $BACKUP_DIR -name "varel_production_*.sql.gz" -mtime +7 -delete

echo "Backup completed: varel_production_$DATE.sql.gz"
# Make executable
sudo chmod +x /usr/local/bin/backup-database.sh

# Add to crontab (daily at 2 AM)
sudo crontab -e
0 2 * * * /usr/local/bin/backup-database.sh

Application Deployment

Install V Language

# Clone V
cd /tmp
git clone https://github.com/vlang/v
cd v
make

# Install system-wide
sudo ./v symlink

# Verify
v version

Deploy Application

# Clone your application
sudo -u varel git clone https://github.com/yourusername/your-app.git /opt/varel/app
cd /opt/varel/app

# Build production binary
sudo -u varel v -prod -o /opt/varel/app/varel_app main.v

# Make executable
sudo chmod +x /opt/varel/app/varel_app

Configuration Files

Create /opt/varel/app/config/production.toml:

[app]
name = "varel"
env = "production"  # Enables template caching for performance

[server]
host = "127.0.0.1"  # Only localhost (Caddy will proxy)
port = "8080"
read_timeout_secs = 30
write_timeout_secs = 30
max_read = 5000000     # 5MB for file uploads
max_write = 16384      # 16KB for responses

[database]
host = "localhost"
port = 5432
database = "varel_production"
user = "varel"
password = ""      # Password loaded from environment variable (DB_PASSWORD)
sslmode = "require"    # Require SSL in production
connect_timeout = 30   # Connection timeout in seconds

# Note: v0.4.0 uses single-process architecture (one database connection)

[session]
secret_key = "your-generated-secret-here"  # Generate with: varel secret generate
storage = "cookie"    # cookie (stateless) or postgres (server-side)
cookie_name = "varel_session"
max_age = 86400       # 24 hours
http_only = true      # Prevent JavaScript access
secure = true         # HTTPS only in production
same_site = "strict"  # CSRF protection
domain = ""           # Leave empty for current domain
path = "/"            # Cookie path scope

[upload]
max_size = 10485760   # 10MB max file size
path = "storage/uploads"

[log]
level = "info"        # debug, info, warn, error
path = "storage/logs/app.log"

Create /opt/varel/app/.env:

# Production environment variables
DB_PASSWORD=secure_random_password_here
SESSION_SECRET=another_secure_random_string_min_32_chars
CSRF_SECRET=yet_another_secure_random_string_min_32_chars
# Secure the .env file
sudo chmod 600 /opt/varel/app/.env
sudo chown varel:varel /opt/varel/app/.env

Run Migrations

# Run migrations
sudo -u varel bash -c 'cd /opt/varel/app && ./varel_app db migrate'

Caddy Configuration

Install Caddy

# Install Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy

# Verify
caddy version

Configure Caddy

Create /etc/caddy/Caddyfile:

# Replace with your domain
yourdomain.com {
    # Reverse proxy to Varel app
    reverse_proxy localhost:8080 {
        # Health checks
        health_uri /health
        health_interval 10s
        health_timeout 5s
    }

    # Security headers
    header {
        # Enable HSTS
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

        # Prevent clickjacking
        X-Frame-Options "SAMEORIGIN"

        # Prevent MIME sniffing
        X-Content-Type-Options "nosniff"

        # Enable XSS protection
        X-XSS-Protection "1; mode=block"

        # Referrer policy
        Referrer-Policy "strict-origin-when-cross-origin"
    }

    # Logging
    log {
        output file /var/log/caddy/access.log
        format json
    }

    # Enable compression (in addition to Varel's)
    encode gzip

    # Rate limiting (optional)
    rate_limit {
        zone dynamic {
            key {remote_host}
            events 100
            window 1m
        }
    }
}

# Redirect www to non-www
www.yourdomain.com {
    redir https://yourdomain.com{uri} permanent
}

Start Caddy

# Reload Caddy with new config
sudo systemctl reload caddy

# Check status
sudo systemctl status caddy

# View logs
sudo journalctl -u caddy -f

Systemd Service

Create Service File

Create /etc/systemd/system/varel.service:

[Unit]
Description=Varel Web Application
After=network.target postgresql.service
Requires=postgresql.service

[Service]
Type=simple
User=varel
Group=varel
WorkingDirectory=/opt/varel/app
EnvironmentFile=/opt/varel/app/.env
ExecStart=/opt/varel/app/varel_app
Restart=always
RestartSec=5

# Security hardening
PrivateTmp=true
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/varel/app/uploads /opt/varel/app/logs

# Resource limits
LimitNOFILE=65536
LimitNPROC=4096

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=varel

[Install]
WantedBy=multi-user.target

Start Service

# Reload systemd
sudo systemctl daemon-reload

# Enable service (start on boot)
sudo systemctl enable varel

# Start service
sudo systemctl start varel

# Check status
sudo systemctl status varel

# View logs
sudo journalctl -u varel -f

Service Management

# Restart service
sudo systemctl restart varel

# Stop service
sudo systemctl stop varel

# Reload service (graceful restart)
sudo systemctl reload varel

# View last 100 lines
sudo journalctl -u varel -n 100

# Follow logs
sudo journalctl -u varel -f

Monitoring

System Monitoring

# Check service status
sudo systemctl status varel
sudo systemctl status caddy
sudo systemctl status postgresql

# Check resource usage
htop

# Check disk usage
df -h

# Check memory usage
free -h

# Check network connections
sudo netstat -tulpn | grep LISTEN

Application Logs

# View application logs
sudo journalctl -u varel -f

# View Caddy logs
sudo tail -f /var/log/caddy/access.log

# View PostgreSQL logs
sudo tail -f /var/log/postgresql/postgresql-14-main.log

Health Checks

# Check application health
curl http://localhost:8080/health

# Check via Caddy (HTTPS)
curl https://yourdomain.com/health

# Check database
sudo -u postgres psql -c "SELECT 1"

Troubleshooting

Application Won't Start

# Check service status
sudo systemctl status varel

# View full logs
sudo journalctl -u varel -n 100 --no-pager

# Check if port is in use
sudo netstat -tulpn | grep 8080

# Test binary directly
sudo -u varel /opt/varel/app/varel_app

Database Connection Errors

# Check PostgreSQL status
sudo systemctl status postgresql

# Test database connection
sudo -u varel psql -h localhost -U varel -d varel_production

# Check pg_hba.conf
sudo nano /etc/postgresql/14/main/pg_hba.conf

# View PostgreSQL logs
sudo tail -f /var/log/postgresql/postgresql-14-main.log

Caddy Issues

# Check Caddy status
sudo systemctl status caddy

# Test Caddy configuration
sudo caddy validate --config /etc/caddy/Caddyfile

# View Caddy logs
sudo journalctl -u caddy -f

# Check certificate
sudo caddy list-certificates

High CPU Usage

# Check what's using CPU
htop

# Check slow queries
sudo -u postgres psql varel_production
SELECT * FROM pg_stat_activity WHERE state = 'active';

Out of Memory

# Check memory usage
free -h

# Check application memory
ps aux | grep varel_app

# Add swap if needed
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

Deployment Checklist

Pre-Deployment

  • Domain DNS configured (A/AAAA records)
  • Server secured (firewall, SSH keys)
  • Database created and migrated
  • Environment variables set
  • Application compiled in production mode
  • Secrets changed from defaults

Post-Deployment

  • Application accessible via HTTPS
  • Health check returns 200 OK
  • Logs are being written
  • Database connections working
  • CSRF tokens working
  • File uploads working (if applicable)
  • Monitoring configured
  • Backups scheduled

Regular Maintenance

  • Weekly: Check logs for errors
  • Weekly: Review security updates
  • Monthly: Check disk usage
  • Monthly: Review database performance
  • Monthly: Test database restore
  • Quarterly: Update dependencies
  • Quarterly: Security audit

Summary

You've learned:

✅ Production architecture with Caddy + Varel + PostgreSQL ✅ Server setup and security configuration ✅ Database installation and backups ✅ Application deployment process ✅ Caddy configuration with automatic HTTPS ✅ Systemd service management ✅ Monitoring and troubleshooting

Your Varel application is now ready for production! 🚀


Additional Resources


Need help? Check the Varel GitHub issues or join the V language Discord!