Skip to content

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.