Deploying Strapi on VPS with Docker and Caddy
In this guide, I'll explore how to deploy Strapi on a Virtual Private Server (VPS) using Docker and Caddy as a reverse proxy.
Prerequisites
Before we begin, ensure you have the following: - A VPS running Ubuntu 22.04 - SSH access to your server - A registered domain name pointing to your VPS - Basic knowledge of Docker and Linux commands
Understanding the Architecture
Our deployment architecture consists of several key components working together: - Strapi: The headless CMS running in a Docker container - PostgreSQL: The database system, also containerized - Caddy: A modern, automatic HTTPS reverse proxy - Docker Compose: For orchestrating our container setup
Initial Server Setup
First, let's prepare our VPS with the necessary software and security configurations. Start by updating your system:
sudo apt update
sudo apt upgrade -y
Install essential tools and Docker dependencies:
sudo apt install -y curl git ufw
Set up UFW (Uncomplicated Firewall) to secure your server:
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
Installing Docker and Docker Compose
Install Docker using the official repository:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
Add your user to the docker group to avoid using sudo with docker commands:
sudo usermod -aG docker $USER
Install Docker Compose:
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
Project Structure
Create a new directory for your project and set up the following structure:
mkdir strapi-deployment
cd strapi-deployment
mkdir -p data/postgres data/strapi config
Docker Compose Configuration
Create a docker-compose.yml
file in your project root:
version: '3'
services:
strapi:
image: strapi/strapi
container_name: strapi
restart: unless-stopped
environment:
DATABASE_CLIENT: postgres
DATABASE_HOST: postgres
DATABASE_PORT: 5432
DATABASE_NAME: strapi
DATABASE_USERNAME: strapi
DATABASE_PASSWORD: your_secure_password
NODE_ENV: production
volumes:
- ./data/strapi:/srv/app
depends_on:
- postgres
postgres:
image: postgres:13
container_name: postgres
restart: unless-stopped
environment:
POSTGRES_DB: strapi
POSTGRES_USER: strapi
POSTGRES_PASSWORD: your_secure_password
volumes:
- ./data/postgres:/var/lib/postgresql/data
caddy:
image: caddy:2
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./data/caddy_data:/data
- ./data/caddy_config:/config
Caddy Configuration
Create a Caddyfile
in your project root:
your-domain.com {
reverse_proxy strapi:1337
log {
output file /var/log/caddy/access.log
format single_field common_log
}
encode gzip
header {
# Enable HTTP Strict Transport Security (HSTS)
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Disable FLoC tracking
Permissions-Policy "interest-cohort=()"
# Enable XSS protection
X-XSS-Protection "1; mode=block"
# Prevent clickjacking
X-Frame-Options "DENY"
# Disable MIME type sniffing
X-Content-Type-Options "nosniff"
# Remove server header
-Server
}
}
Environment Variables
Create a .env
file for sensitive information:
POSTGRES_PASSWORD=your_secure_password
STRAPI_PASSWORD=your_secure_password
DOMAIN=your-domain.com
EMAIL=your-email@example.com
Deployment Process
Start the deployment by pulling the necessary Docker images:
docker-compose pull
Launch the services:
docker-compose up -d
Monitoring and Maintenance
Monitor your containers' health using Docker commands:
docker-compose ps
docker-compose logs -f
Backup Strategy
Create a backup script backup.sh
:
#!/bin/bash
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
BACKUP_DIR="/path/to/backups"
# Backup PostgreSQL database
docker exec postgres pg_dump -U strapi strapi > "$BACKUP_DIR/database_$TIMESTAMP.sql"
# Backup Strapi files
tar -czf "$BACKUP_DIR/strapi_files_$TIMESTAMP.tar.gz" ./data/strapi
Make the script executable:
chmod +x backup.sh
Security Considerations
Enhance your deployment's security by implementing these measures:
# Update Caddy configuration with security headers
your-domain.com {
reverse_proxy strapi:1337
encode gzip
tls your-email@example.com
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
Troubleshooting Common Issues
When encountering issues, check the following:
# View container logs
docker-compose logs strapi
docker-compose logs postgres
docker-compose logs caddy
# Check container status
docker-compose ps
# Restart services
docker-compose restart strapi
Performance Optimization
Optimize your Strapi deployment by adjusting the Node.js memory limits in your Docker Compose file:
services:
strapi:
environment:
NODE_OPTIONS: "--max-old-space-size=2048"
Scaling Considerations
For higher traffic scenarios, consider implementing:
- Redis for caching
- Content Delivery Network (CDN) integration
- Database replication
- Container orchestration with Docker Swarm or Kubernetes
Conclusion
You now have a production-ready Strapi deployment using Docker and Caddy. This setup provides automatic HTTPS, robust database management, and easy maintenance capabilities. Regular monitoring and backups will ensure your CMS remains reliable and secure.