WordPress & n8n: Self-Host Your Website and Automation on VPS

Complete guide to self-hosting WordPress and n8n on your VPS. Learn how to deploy the world's most popular CMS alongside powerful workflow automation with Docker Compose, SSL, and production best practices.

Adrian Silaghi

15 min read

818 views

wordpressn8nvpsself-hostingdockerautomationcms

Pricing checked 2 November 2026: The example VPS is 3 shared vCPU/4 GB RAM/60 GB storage at €9.49/month (€113.88 for 12 full months), excluding VAT and extras. This is a different allocation from the older 2 vCPU/4 GB examples used by other providers in the table. Current VPS plans.

Self-hosting gives you full control over your website and automation workflows. This guide shows you how to deploy WordPress and n8n on a single VPS using Docker Compose—giving you a powerful content management system and workflow automation platform for a fraction of the cost of managed services.

Why Self-Host WordPress and n8n?

WordPress Benefits

  • Full Control: No platform restrictions, install any plugin or theme
  • Cost comparison: Compare your required managed WordPress plan with VPS, backup and maintenance costs for the same workload.
  • Performance: Dedicated resources with no noisy neighbors
  • Privacy: Your data stays on your server in your chosen location
  • Customization: PHP settings, caching, and server configuration

n8n Benefits

  • Execution capacity: Self-hosted n8n uses your server resources. Concurrency, memory, database performance and third-party API limits bound the workload it can handle; review the n8n licence and features you need.
  • Data privacy: You control the server-side workflow storage, but workflow nodes, webhooks, email, telemetry and external APIs can transmit data. Review those flows, credentials, processors and retention before handling sensitive data.
  • Custom Integrations: Install community nodes and custom code
  • Operating cost: Self-hosting adds server, backup, monitoring and maintenance costs. Compare these with the current n8n Cloud quote for your execution volume and required features.
  • Full API Access: Integrate with any internal system

What You'll Build

By the end of this guide, you'll have:

  • ✅ WordPress with MySQL database and persistent storage
  • ✅ n8n workflow automation with SQLite/PostgreSQL backend
  • ✅ Nginx reverse proxy handling both applications
  • ✅ Free SSL certificates via Let's Encrypt
  • ✅ Automatic container restarts and health checks
  • ✅ Automated backup system

Prerequisites

VPS Requirements

  • OS: Ubuntu 22.04 or 24.04 LTS
  • RAM: 2GB minimum (4GB recommended for both services)
  • Storage: 40GB+ NVMe SSD
  • CPU: 2 vCPUs minimum

Example starting configuration: DD Maliuc Shared provides 3 shared vCPU, 4 GB RAM and 60 GB NVMe for €9.49/month, billed hourly and excluding VAT. Load-test WordPress and your n8n workflows together; memory, execution concurrency and database size may require a larger VPS.

Domain Setup

  • Domain for WordPress (e.g., example.com, www.example.com)
  • Subdomain for n8n (e.g., n8n.example.com or automation.example.com)
  • Both domains pointed to your VPS IP via A records

Step 1: Install Docker and Docker Compose

SSH into your VPS and run:

# 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 your user to docker group (log out and back in after)
sudo usermod -aG docker $USER

# Install Docker Compose plugin
sudo apt install docker-compose-plugin -y

# Verify installation
docker --version
docker compose version

Log out and back in for group changes to take effect:

exit
# SSH back in

Step 2: Create Project Structure

# Create main project directory
mkdir -p ~/hosting && cd ~/hosting

# Create subdirectories
mkdir -p nginx/conf.d nginx/ssl
mkdir -p wordpress/wp-content
mkdir -p n8n/data
mkdir -p mysql/data
mkdir -p backups

Your directory structure:

~/hosting/
├── docker-compose.yml
├── .env
├── nginx/
│   ├── conf.d/
│   │   ├── wordpress.conf
│   │   └── n8n.conf
│   └── ssl/
├── wordpress/
│   └── wp-content/
├── n8n/
│   └── data/
├── mysql/
│   └── data/
└── backups/

Step 3: Create Environment File

Create .env file with your configuration:

# Domain Configuration
WORDPRESS_DOMAIN=example.com
N8N_DOMAIN=n8n.example.com

# MySQL Configuration
MYSQL_ROOT_PASSWORD=your_secure_root_password_here
MYSQL_DATABASE=wordpress
MYSQL_USER=wordpress
MYSQL_PASSWORD=your_secure_wordpress_password_here

# WordPress Configuration
WORDPRESS_TABLE_PREFIX=wp_

# n8n Configuration
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=your_secure_n8n_password_here
N8N_ENCRYPTION_KEY=your_32_char_encryption_key_here

# Generate secure passwords with:
# openssl rand -base64 32

Security tip: Generate strong passwords:

openssl rand -base64 32

Step 4: Create Docker Compose File

Create docker-compose.yml:

version: '3.8'

services:
  # Nginx Reverse Proxy
  nginx:
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
      - ./wordpress:/var/www/wordpress
    networks:
      - hosting
    depends_on:
      - wordpress
      - n8n

  # WordPress
  wordpress:
    image: wordpress:6.7-php8.3-fpm
    container_name: wordpress
    restart: unless-stopped
    environment:
      WORDPRESS_DB_HOST: mysql
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
      WORDPRESS_TABLE_PREFIX: ${WORDPRESS_TABLE_PREFIX}
    volumes:
      - ./wordpress:/var/www/html
    networks:
      - hosting
    depends_on:
      mysql:
        condition: service_healthy

  # MySQL Database
  mysql:
    image: mysql:8.0
    container_name: mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - ./mysql/data:/var/lib/mysql
    networks:
      - hosting
    command: --default-authentication-plugin=mysql_native_password
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

  # n8n Workflow Automation
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    environment:
      - N8N_HOST=${N8N_DOMAIN}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://${N8N_DOMAIN}/
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - GENERIC_TIMEZONE=Europe/Berlin
      - TZ=Europe/Berlin
    volumes:
      - ./n8n/data:/home/node/.n8n
    networks:
      - hosting
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:5678/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  # Redis (optional - for WordPress object caching)
  redis:
    image: redis:7-alpine
    container_name: redis
    restart: unless-stopped
    volumes:
      - redis-data:/data
    networks:
      - hosting
    command: redis-server --appendonly yes
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

networks:
  hosting:
    driver: bridge

volumes:
  redis-data:

Step 5: Configure Nginx for WordPress

Create nginx/conf.d/wordpress.conf:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    root /var/www/wordpress;
    index index.php index.html;

    # SSL Configuration
    ssl_certificate /etc/nginx/ssl/wordpress/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/wordpress/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # Security Headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Gzip Compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;

    # Upload size limit (important for WordPress media)
    client_max_body_size 64M;

    # WordPress permalinks
    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    # PHP handling via WordPress container
    location ~ .php$ {
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_read_timeout 300;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
    }

    # Block access to sensitive files
    location ~ /. {
        deny all;
    }

    location ~* /(?:uploads|files)/.*.php$ {
        deny all;
    }

    location = /wp-config.php {
        deny all;
    }

    # Cache static assets
    location ~* .(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot|webp)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # Block xmlrpc.php (common attack vector)
    location = /xmlrpc.php {
        deny all;
        access_log off;
        log_not_found off;
    }
}

Step 6: Configure Nginx for n8n

Create nginx/conf.d/n8n.conf:

server {
    listen 80;
    server_name n8n.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name n8n.example.com;

    # SSL Configuration
    ssl_certificate /etc/nginx/ssl/n8n/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/n8n/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # Security Headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;

    # Proxy to n8n
    location / {
        proxy_pass http://n8n:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 86400;
        proxy_buffering off;
        chunked_transfer_encoding off;
    }

    # Webhook endpoints (important for n8n)
    location /webhook/ {
        proxy_pass http://n8n:5678/webhook/;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
    }

    location /webhook-test/ {
        proxy_pass http://n8n:5678/webhook-test/;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
    }
}

Step 7: Obtain SSL Certificates

Stop any services using port 80 and get certificates:

# Install certbot
sudo apt install certbot -y

# Get certificate for WordPress domain
sudo certbot certonly --standalone -d example.com -d www.example.com

# Get certificate for n8n domain
sudo certbot certonly --standalone -d n8n.example.com

# Create SSL directories
mkdir -p ~/hosting/nginx/ssl/wordpress
mkdir -p ~/hosting/nginx/ssl/n8n

# Copy certificates
sudo cp /etc/letsencrypt/live/example.com/fullchain.pem ~/hosting/nginx/ssl/wordpress/
sudo cp /etc/letsencrypt/live/example.com/privkey.pem ~/hosting/nginx/ssl/wordpress/
sudo cp /etc/letsencrypt/live/n8n.example.com/fullchain.pem ~/hosting/nginx/ssl/n8n/
sudo cp /etc/letsencrypt/live/n8n.example.com/privkey.pem ~/hosting/nginx/ssl/n8n/

# Fix permissions
sudo chown -R $USER:$USER ~/hosting/nginx/ssl/

Automatic Certificate Renewal

Create renewal script ~/hosting/renew-certs.sh:

#!/bin/bash
certbot renew --quiet

# Copy renewed certificates
cp /etc/letsencrypt/live/example.com/fullchain.pem /home/$USER/hosting/nginx/ssl/wordpress/
cp /etc/letsencrypt/live/example.com/privkey.pem /home/$USER/hosting/nginx/ssl/wordpress/
cp /etc/letsencrypt/live/n8n.example.com/fullchain.pem /home/$USER/hosting/nginx/ssl/n8n/
cp /etc/letsencrypt/live/n8n.example.com/privkey.pem /home/$USER/hosting/nginx/ssl/n8n/

# Reload nginx
docker exec nginx nginx -s reload
chmod +x ~/hosting/renew-certs.sh

# Add to crontab (runs twice daily)
(crontab -l 2>/dev/null; echo "0 3,15 * * * /home/$USER/hosting/renew-certs.sh") | crontab -

Step 8: Launch Your Stack

cd ~/hosting

# Start all services
docker compose up -d

# Check status
docker compose ps

# View logs
docker compose logs -f

Expected output:

NAME        SERVICE     STATUS    PORTS
mysql       mysql       running   3306/tcp
nginx       nginx       running   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
n8n         n8n         running   5678/tcp
redis       redis       running   6379/tcp
wordpress   wordpress   running   9000/tcp

Step 9: Complete WordPress Setup

Visit https://example.com and complete the WordPress installation wizard:

  1. Select your language
  2. Enter site title, admin username, and password
  3. Enter your email address
  4. Click "Install WordPress"

Recommended WordPress Plugins

Install these essential plugins:

  • Redis Object Cache: Connect to your Redis container for faster caching
  • Wordfence Security: Firewall and malware scanner
  • UpdraftPlus: Automated backups to cloud storage
  • WP Super Cache or W3 Total Cache: Page caching
  • Yoast SEO: Search engine optimization

Configure Redis Object Cache

Add to wp-config.php (before "That's all, stop editing!"):

define('WP_REDIS_HOST', 'redis');
define('WP_REDIS_PORT', 6379);
define('WP_CACHE', true);

Then install and activate the "Redis Object Cache" plugin and enable it.

Step 10: Access n8n

Visit https://n8n.example.com and log in with your credentials from the .env file.

First Workflow: WordPress to n8n Integration

Create a workflow that monitors your WordPress site:

  1. Add Webhook trigger node
  2. Copy the webhook URL
  3. In WordPress, install the WP Webhooks plugin
  4. Configure it to send new post notifications to your n8n webhook
  5. Add actions: email notification, Slack message, social media post

Popular n8n Use Cases

  • Content Publishing: Auto-post WordPress content to social media
  • Lead Management: Send form submissions to CRM
  • Backup Notifications: Alert when backups complete or fail
  • Uptime Monitoring: Check WordPress health and alert on issues
  • Comment Moderation: Auto-approve or flag comments with AI
  • Analytics Reports: Weekly traffic summaries via email

Backup Strategy

Create ~/hosting/backup.sh:

#!/bin/bash
BACKUP_DIR="/home/$USER/hosting/backups"
DATE=$(date +%Y%m%d_%H%M%S)

# Create backup directory
mkdir -p $BACKUP_DIR

# Backup MySQL
docker exec mysql mysqldump -uroot -p"${MYSQL_ROOT_PASSWORD}" --all-databases > $BACKUP_DIR/mysql_$DATE.sql

# Backup WordPress files
tar -czf $BACKUP_DIR/wordpress_$DATE.tar.gz -C /home/$USER/hosting/wordpress .

# Backup n8n data
tar -czf $BACKUP_DIR/n8n_$DATE.tar.gz -C /home/$USER/hosting/n8n/data .

# Delete backups older than 7 days
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete

echo "Backup completed: $DATE"
chmod +x ~/hosting/backup.sh

# Schedule daily backups at 2 AM
(crontab -l 2>/dev/null; echo "0 2 * * * /home/$USER/hosting/backup.sh") | crontab -

Performance Optimization

WordPress Optimization

Add to wp-config.php:

// Increase memory limit
define('WP_MEMORY_LIMIT', '256M');

// Reduce post revisions
define('WP_POST_REVISIONS', 5);

// Disable file editing in admin
define('DISALLOW_FILE_EDIT', true);

// Auto-save interval (seconds)
define('AUTOSAVE_INTERVAL', 120);

PHP Optimization

Create php-custom.ini and mount in WordPress container:

upload_max_filesize = 64M
post_max_size = 64M
memory_limit = 256M
max_execution_time = 300
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0

Monitoring & Health Checks

Container Health

# Check all container status
docker compose ps

# View resource usage
docker stats

# Check specific container logs
docker compose logs -f wordpress
docker compose logs -f n8n

Simple Uptime Monitoring with n8n

Create a workflow in n8n:

  1. Add Schedule Trigger (every 5 minutes)
  2. Add HTTP Request node to check https://example.com
  3. Add IF node to check response status
  4. Add Email or Slack node for alerts

Security Hardening

Firewall Setup

# Enable UFW
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable

Fail2Ban for WordPress

sudo apt install fail2ban -y

# Create WordPress jail
sudo nano /etc/fail2ban/jail.d/wordpress.conf

Add:

[wordpress]
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 5
bantime = 3600

Troubleshooting Common Issues

Issue Cause Solution
WordPress white screen PHP memory limit Increase WP_MEMORY_LIMIT in wp-config.php
n8n webhooks not working WEBHOOK_URL misconfigured Verify WEBHOOK_URL in .env matches your domain
SSL certificate errors Certificate path wrong Check nginx ssl paths and file permissions
Database connection refused MySQL not ready Wait for MySQL health check to pass
Uploads failing client_max_body_size too low Increase in nginx.conf (default 64M)
n8n losing workflows Volume not persisted Verify n8n/data volume mount

Cost Comparison: Self-Hosted vs Managed

Service Managed Cost Self-Hosted (DanubeData VPS) Savings
WordPress Hosting $30-100/month (WP Engine, Kinsta) Included $360-1200/year
n8n Cloud Pro $20-50/month Included $240-600/year
DanubeData VPS (3 shared vCPU, 4GB / 60GB) N/A €9.49/month €113.88/year
Total Annual Cost $600-1,800/year €113.88/year Compare full costs in one currency

Why DanubeData VPS for WordPress & n8n?

Feature DanubeData VPS DigitalOcean Linode
4GB RAM tier (allocations vary; DD: 3 shared vCPU/60GB) €9.49/mo $24/mo $24/mo
Storage 80GB NVMe 80GB SSD 80GB SSD
Bandwidth 20TB 4TB 4TB
Location German hosting; review full data flows Global Global
Setup Time < 60 seconds ~2 minutes ~2 minutes
Snapshots Included $2.40/mo $2.50/mo

Get Started Now

  1. Create your DanubeData account
  2. Deploy a VPS with Ubuntu 24.04 (3 shared vCPU, 4GB / 60GB recommended)
  3. Point your domains to the VPS IP
  4. Follow this guide to install WordPress and n8n
  5. Optional: Add managed MySQL and Redis for even better performance

Perfect for: Bloggers, small businesses, agencies, developers, and anyone who wants full control over their web presence and automation workflows.

Questions about self-hosting WordPress or n8n? Contact our team or explore our documentation for more guides.

Browse all articles
Tutorials7 min read

Raport zilnic de stoc WooCommerce cu n8n

Importă un workflow n8n pentru raportul de stoc WooCommerce: paginare, variații, praguri și controlul retrimiterilor, cu acces doar de citire în magazin.

Adrian Silaghi55 views

Ready to get started?

Deploy your infrastructure in minutes with DanubeData's managed services.