Deployment Guide
This comprehensive guide covers deploying Varel applications to production with Caddy, PostgreSQL, and systemd.
Table of Contents
- Architecture Overview
- Server Setup
- Database Setup
- Application Deployment
- Caddy Configuration
- Systemd Service
- Monitoring
- Troubleshooting
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
- Caddy Documentation: https://caddyserver.com/docs/
- PostgreSQL Documentation: https://www.postgresql.org/docs/
- Systemd Documentation: https://www.freedesktop.org/wiki/Software/systemd/
- Let's Encrypt: https://letsencrypt.org/
- Security Best Practices: https://cheatsheetseries.owasp.org/
Need help? Check the Varel GitHub issues or join the V language Discord!