Tag: wordpress

  • Deploy WordPress Locally with Docker — A Quick Start Guide

    If you want to learn, test, or develop WordPress without spending money on a server or domain — and without exposing your site to the public internet — running WordPress locally is the way to go. A local setup lets you simulate a full website environment right on your own machine, accessible through your browser with no internet connection or file uploads required. Building on the Local approach covered earlier, this lesson introduces a faster, more modern deployment method using Docker.


    1. Why Run WordPress Locally?

    1.1 Learn and Explore WordPress — Fast and Free

    One of the biggest perks of a local WordPress setup is that you can dive in and start experimenting immediately — no hosting plan, no domain registration, no cost, no risk.

    You can install different versions of WordPress side by side and compare how things have changed. Try out different themes and plugins to explore layouts and functionality. Build different types of sites — a blog, an online store, a community forum — and learn how each one works from the ground up.

    💡 Learning Scenario Examples:

    Learning GoalHow to Practice Locally
    Compare WordPress versionsRun WordPress 6.7 and 6.8 simultaneously and compare block editor improvements
    Get started with theme developmentInstall the official Twenty Twenty-Five theme and gradually modify its CSS and template files
    Test plugin compatibilityTest combinations of WooCommerce, Elementor, and other plugins in an isolated environment
    Multisite networksEnable WordPress Multisite to simulate enterprise-level multi-site management

    1.2 Develop and Test in a Safe Sandbox

    Another major benefit is having a safe, private space to experiment with your site’s design, content, plugins, and code — without ever risking your live site.

    Edit styles, rearrange layouts, add or remove content and images, and see results in real time — no uploading files or waiting for deploys. Install and test plugins to check for compatibility issues, fine-tune their settings, and optimize performance. Write and debug custom code in a controlled environment where mistakes won’t take down a production site.

    ⚠️ A Cautionary Tale: Countless WordPress sites have crashed because someone updated a plugin directly in production. A local testing environment can save you from that kind of disaster.

    1.3 Back Up and Restore with Ease

    A local environment also makes it far easier to keep your data safe. You can back up your site’s files and database at any time — manually or on a schedule — and store them on your machine or an external drive. If anything goes wrong, restoring is as simple as copying the backup files back into place. No reinstallation, no reconfiguration.

    🔄 The Docker Backup Advantage:

    Compared to traditional local environments like XAMPP or MAMP, Docker offers a much cleaner backup story. Your entire WordPress site — themes, plugins, uploaded media, and database — can be version-controlled and migrated as a single, self-contained unit.

    1.4 Seamlessly Migrate Between Local and Production

    Finally, a local setup makes it straightforward to move your site to a live server when you’re ready — or pull a production site down to your local machine for maintenance. Once development and testing are done, upload your files and database to a server, tweak a few settings, and your site is live. The reverse works just as easily for backup or continued development.

    📦 Migration Workflow Overview:

    ┌─────────────────┐    Export    ┌─────────────────┐    Import    ┌─────────────────┐
    │  Local Dev Env   │ ─────────▶ │  Migration Pkg   │ ─────────▶ │ Production Server│
    │ (Docker Container)│           │ (.sql + files)   │            │  (Cloud Server)  │
    └─────────────────┘            └─────────────────┘            └─────────────────┘

    2. Technical Foundations & Tool Selection

    Before diving into the actual deployment, let’s cover the core technical concepts behind this lesson. Understanding these fundamentals will make the hands-on steps much easier to follow.

    2.1 Docker: Modern Application Containerization

    Docker is a containerization technology that packages an application along with all its dependencies into a standardized unit called a “container.” Think of a Docker container as a lightweight virtual machine — but one that starts faster and uses far fewer resources than a traditional VM.

    🎯 Why Use Docker to Deploy WordPress?

    ComparisonTraditional (XAMPP/MAMP)Docker
    Environment consistencyLocal and server environments may differDev, staging, and production environments are identical
    Dependency managementManually install PHP, MySQL, etc.All dependencies handled automatically
    Multiple versionsDifficult; conflicts are commonRun multiple isolated environments with ease
    Cleanup & rebuildLeftover files are hard to removeDelete the container and everything is gone
    Migration & deploymentRequires reconfigurationExport an image and you’re done
    Resource usageServices run in the background constantlyStart and stop on demand — zero footprint when idle

    2.2 Docker Compose: Orchestrating Multiple Containers

    Docker Compose is the official multi-container orchestration tool from Docker. A complete WordPress site needs several services working together: a web server, a PHP runtime, a database, and a caching layer. Docker Compose lets you define and manage all of these interconnected services in a single YAML configuration file.

    📝 Key Concepts Explained:

    # docker-compose.yml basic structure
    services:        # Define the services (containers) to run
      wordpress:     # Service name
        image: wordpress:fpm   # Docker image to use
        ports:
          - "80:80"            # Port mapping: host_port:container_port
        volumes:
          - wp_data:/var/www/html # Persist data across restarts
        environment:             # Environment variable configuration
          WORDPRESS_DB_HOST: db
        depends_on:              # Service dependencies
          - db
    
      db:            # Database service
        image: mariadb:latest
        volumes:
          - db_data:/var/lib/mysql
    
    volumes:         # Declare volumes for persistent storage
      wp_data:
      db_data:

    ⚡ Important Note: Starting with Docker Compose V2 (now the default), the **version: "3"** declaration at the top of the config file has been deprecated. Keeping it won’t cause errors, but for new projects it’s best to simply leave it out. All configuration files in this course follow the latest convention.

    2.3 Choosing the Right Docker Setup for Your OS

    Different operating systems call for different Docker strategies:

    Operating SystemRecommended SetupNotes
    macOSOrbStackLightweight and efficient; provides a virtualized environment
    LinuxNative Docker + DockgeBest performance; GUI management available
    HomeLab usersUmbrel / CasaOSGraphical management interface; great for beginners

    3. macOS Setup: OrbStack + Docker + Dockge

    As container technology continues to evolve, developers increasingly demand lightweight and efficient virtualization tools. OrbStack is a next-generation container and virtual machine platform built specifically for macOS, offering a faster and leaner alternative to Docker Desktop. This chapter walks through running Ubuntu on OrbStack, then setting up Docker and Dockge inside it to create a flexible, high-performance container management environment.

    3.1 What Is OrbStack?

    OrbStack is a modern container and VM platform designed for macOS. It’s lighter, faster, and more resource-efficient than Docker Desktop:

    FeatureDetails
    🔋 Low resource usageSignificantly lower CPU and memory footprint vs. Docker Desktop (real-world memory usage is roughly 1/3 to 1/5)
    Fast startupNear-instant container and VM launches (cold start typically under 2 seconds)
    🔗 Deep macOS integrationBuilt-in file sharing and port forwarding; container services accessible via container-name.orb.local
    🐳 Docker & Kubernetes supportFully compatible with Docker commands and workflows — no changes to existing Docker Compose files needed

    💡 When to choose OrbStack: If you’re on macOS and primarily doing web development or running Docker containers, OrbStack is arguably the best option available today.

    3.2 Why Run Ubuntu Inside OrbStack?

    Running an Ubuntu VM on OrbStack and deploying Docker inside it offers several advantages:

    • 🔒 Environment isolation: The Ubuntu VM provides a fully isolated environment, preventing any container side effects from affecting your macOS host
    • 🎛️ Flexibility: Choose a specific Ubuntu release (e.g., Ubuntu 22.04 LTS or 24.04 LTS)
    • Compatibility: Solves compatibility issues for applications that depend on Linux kernel features
    • 📦 Portability: The entire environment can be easily backed up and migrated; VMs can be exported in standard formats for team sharing

    3.3 Step-by-Step Setup

    Step 1: Install OrbStack

    Option A: Install via Homebrew (Recommended)

    brew install orbstack

    Option B: Download from the official site
    Visit https://orbstack.dev/download and grab the installer for your chip (Apple Silicon / Intel).

    🔍 Verify the installation: Open a terminal and run **orb version** to confirm everything is set up correctly.

    Step 2: Create an Ubuntu Virtual Machine

    Once OrbStack is running, create an Ubuntu VM through either the GUI or the command line:

    # Create an Ubuntu VM named "ubuntu"
    orb create ubuntu ubuntu
    
    # Enter the VM's shell
    orb shell ubuntu

    💡 Tip: Linux VMs created by OrbStack share the macOS host’s file system. Your Mac user directory is automatically mounted at **/Users/your-username** inside the VM.

    Step 3: Install Docker Inside Ubuntu

    Once you’re inside the Ubuntu VM, install Docker with the following steps:

    # 1. Update the package index and install required dependencies
    sudo apt update
    sudo apt install apt-transport-https ca-certificates curl software-properties-common -y
    
    # 2. Add Docker's official GPG key
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
    
    # 3. Add the Docker repository to APT sources
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    # 4. Install Docker Engine and related components
    sudo apt update
    sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
    
    # 5. Verify the installation
    sudo docker --version

    Step 4: Deploy Dockge — A Visual Docker Compose Manager

    Dockge is a lightweight, open-source Docker Compose management tool created by the developer behind Uptime Kuma. Compared to heavier solutions like Portainer, Dockge focuses exclusively on managing Docker Compose projects with a cleaner, more intuitive interface.

    # 1. Create the required directories
    sudo mkdir -p /opt/stacks /opt/dockge
    cd /opt/dockge
    
    # 2. Download the official compose.yaml
    sudo curl https://raw.githubusercontent.com/louislam/dockge/master/compose.yaml --output compose.yaml
    
    # 3. Start Dockge
    sudo docker compose up -d

    📋 Dockge Key Features:

    • 🖥️ Intuitive web UI for managing Docker Compose projects
    • 📝 Built-in YAML editor with syntax highlighting
    • 🔄 One-click start / stop / restart for Compose stacks
    • 📊 Real-time container logs and status monitoring

    Step 5: Access the Dockge Web Interface

    Once Dockge is up and running, configure port forwarding so you can access it from your Mac:

    # Run this in the macOS terminal (not inside the VM)
    orb expose ubuntu 5001:5001

    Then open your browser and navigate to: http://localhost:5001

    3.4 Troubleshooting

    🔧 Issue 1: Port Mapping Not Working

    # Reconfigure port mapping
    orb expose ubuntu 5001:5001

    🔧 Issue 2: Docker Permission Denied

    # Add the current user to the docker group
    sudo usermod -aG docker $USER
    # Log out and back in for the change to take effect

    🔧 Issue 3: Running Out of Disk Space

    # Check disk usage
    df -h
    
    # Clean up unused Docker resources
    docker system prune -a

    3.5 Use Cases

    This nested virtualization approach works well for the following scenarios:

    ScenarioDescription
    🖥️ Development environmentsMaintain a dev/test environment that mirrors production
    📚 Learning & experimentationSafely learn and experiment with Docker technology
    📦 Container managementSimplify Docker Compose project management with Dockge
    🔄 Cross-platform developmentRun Linux-dependent applications on macOS

    4. Deploying WordPress with Docker Compose

    Regardless of which Docker setup you chose, the WordPress deployment process is the same. This chapter provides a battle-tested configuration featuring the full Nginx + PHP-FPM + MariaDB + Redis stack, complete with FastCGI caching for maximum performance.

    4.1 Architecture Overview

    ┌─────────────────────────────────────────────────────────────┐
    │                      Browser Request                         │
    └─────────────────────────┬───────────────────────────────────┘
                              ▼
    ┌─────────────────────────────────────────────────────────────┐
    │                    Nginx (Port 80)                           │
    │              ┌─────────────────────────┐                    │
    │              │   FastCGI Cache Layer   │                    │
    │              │  (Static HTML caching)  │                    │
    │              └───────────┬─────────────┘                    │
    └──────────────────────────┼──────────────────────────────────┘
                               ▼
    ┌─────────────────────────────────────────────────────────────┐
    │              WordPress (PHP-FPM, Port 9000)                  │
    │              ┌─────────────────────────┐                    │
    │              │   Redis Object Cache    │◀───────────────────┤
    │              │  (DB query caching)     │                    │
    │              └───────────┬─────────────┘                    │
    └──────────────────────────┼──────────────────────────────────┘
                               ▼
    ┌─────────────────────────────────────────────────────────────┐
    │                   MariaDB Database                           │
    └─────────────────────────────────────────────────────────────┘

    🎯 Two-Layer Caching Strategy Explained:

    • FastCGI Cache (Nginx layer): Caches the fully rendered HTML pages generated by PHP as static files. Subsequent requests for the same page are served directly from cache — no PHP execution required.
    • Redis Object Cache (WordPress layer): Caches WordPress’s internal database query results, reducing the load on the database.

    4.2 File Structure

    wordpress-docker/
    ├── docker-compose.yml    # Docker Compose configuration
    ├── nginx.conf            # Nginx configuration
    └── logs/
        └── nginx/            # Nginx log directory (auto-created)

    4.3 docker-compose.yml

    services:
      db:
        image: mariadb:latest
        restart: always
        environment:
          MYSQL_ROOT_PASSWORD: somewordpress
          MYSQL_DATABASE: wordpress
          MYSQL_USER: wordpress
          MYSQL_PASSWORD: wordpress
        volumes:
          - db_data:/var/lib/mysql
      redis:
        image: redis:latest
        restart: always
      wordpress:
        depends_on:
          - db
          - redis
        image: wordpress:fpm
        restart: always
        environment:
          WORDPRESS_DB_HOST: db:3306
          WORDPRESS_DB_USER: wordpress
          WORDPRESS_DB_PASSWORD: wordpress
          WORDPRESS_DB_NAME: wordpress
          WORDPRESS_CONFIG_EXTRA: |
            define('WP_REDIS_HOST', 'redis');
            define('WP_REDIS_PORT', 6379);
            define('WP_CACHE', true);
        command:
          - sh
          - -c
          - |
            echo 'upload_max_filesize = 4096M' > /usr/local/etc/php/conf.d/uploads.ini
            echo 'post_max_size = 4096M' >> /usr/local/etc/php/conf.d/uploads.ini
            echo 'memory_limit = 512M' >> /usr/local/etc/php/conf.d/uploads.ini
            echo 'max_execution_time = 1200' >> /usr/local/etc/php/conf.d/uploads.ini
            docker-entrypoint.sh php-fpm
        volumes:
          - wordpress_data:/var/www/html
      nginx:
        depends_on:
          - wordpress
        image: nginx:latest
        restart: always
        ports:
          - 80:80
        volumes:
          - ./nginx.conf:/etc/nginx/conf.d/default.conf
          - wordpress_data:/var/www/html
          - nginx_cache:/var/cache/nginx
          - ./logs/nginx:/var/log/nginx
    volumes:
      db_data: null
      wordpress_data: null
      nginx_cache: null
    networks: {}

    4.4 nginx.conf

    # FastCGI cache definition
    fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WPCACHE:64m inactive=60m max_size=256m;
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
    
    server {
        listen 80;
        server_name localhost;
    
        root /var/www/html;
        index index.php index.html index.htm;
    
        # Set to 0 for unlimited, or explicitly set to 4G
        client_max_body_size 4G;
    
        # Increase buffer size for large uploads to reduce temp file writes
        client_body_buffer_size 10M;
    
        # Extend read timeout to prevent disconnections during large (4GB) uploads
        client_body_timeout 600s;
    
        # Cache status header (useful for debugging — shows whether cache was hit)
        add_header X-Cache-Status $upstream_cache_status;
    
        location / {
            try_files $uri $uri/ /index.php?$args;
        }
    
        location ~ \.php$ {
            try_files $uri =404;
            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 cache settings
            fastcgi_cache WPCACHE;
            fastcgi_cache_valid 200 60m;
            fastcgi_cache_valid 404 1m;
    
            # Conditions for skipping the cache
            set $skip_cache 0;
    
            # Don't cache POST requests
            if ($request_method = POST) {
                set $skip_cache 1;
            }
    
            # Don't cache logged-in users or comment authors
            if ($http_cookie ~* "wordpress_logged_in|comment_author") {
                set $skip_cache 1;
            }
    
            # Don't cache admin pages
            if ($request_uri ~* "/wp-admin/|/wp-login.php") {
                set $skip_cache 1;
            }
    
            fastcgi_cache_bypass $skip_cache;
            fastcgi_no_cache $skip_cache;
        }
    
        # Cache static assets
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
            expires max;
            log_not_found off;
        }
    
        # Block access to hidden files
        location ~ /\.ht {
            deny all;
        }
    }

    4.5 Image Reference

    ServiceImageNotes
    dbmariadb:latestLatest stable MariaDB release
    redisredis:latestLatest stable Redis release
    wordpresswordpress:fpmWordPress PHP-FPM variant (automatically tracks the latest PHP version)
    nginxnginx:latestLatest stable Nginx release

    4.6 Deployment Steps

    # 1. Create the project directory
    mkdir wordpress-docker && cd wordpress-docker
    
    # 2. Create the log directory
    mkdir -p logs/nginx
    
    # 3. Create the docker-compose.yml file
    # (Save the configuration above to this file)
    
    # 4. Create the nginx.conf file
    # (Save the configuration above to this file)
    
    # 5. Start all services
    docker compose up -d
    
    # 6. Check the status
    docker compose ps
    
    # 7. View logs (optional)
    docker compose logs -f

    🎉 Once everything is up, visit http://localhost to launch the WordPress installation wizard!

    You can also use Dockge’s web-based GUI to configure everything visually.

    4.7 Verifying That Caching Works

    # Check the response headers with curl
    curl -I http://localhost
    
    # Look at the X-Cache-Status header:
    # MISS   = Cache miss (first visit)
    # HIT    = Cache hit (served from cache)
    # BYPASS = Cache bypassed (logged-in user or admin page)

    The first request will show **MISS**. Refresh the page and you should see **HIT**, confirming that caching is working.

    4.8 WordPress Admin Configuration

    After deployment, you still need to enable Redis object caching in the WordPress dashboard:

    1. Install the Redis Object Cache plugin
      • Go to WordPress Dashboard → Plugins → Add New
      • Search for “Redis Object Cache”
      • Install and activate it
    2. Enable object caching
      • Navigate to Settings → Redis
      • Click the “Enable Object Cache” button
      • Confirm the status shows “Connected”

    ✅ That’s it — you now have a high-performance local WordPress environment with two-layer caching (FastCGI + Redis)! Upcoming lessons will dive deeper into how caching works under the hood.

    4.9 Common Management Commands

    # Stop all services
    docker compose down
    
    # Stop and remove all data volumes (full reset)
    docker compose down -v
    
    # Restart services
    docker compose restart
    
    # View logs for a specific service
    docker compose logs wordpress
    docker compose logs nginx
    
    # Enter the WordPress container
    docker compose exec wordpress bash
    
    # Enter the database container
    docker compose exec db mysql -u wordpress -p

    5. Installing WordPress on HomeLab Platforms

    Running applications on a personal server or home lab (HomeLab) is a popular choice among tech enthusiasts and privacy-conscious users. Platforms like Umbrel, CasaOS, and other Docker-based HomeLab management tools dramatically simplify the process of deploying and managing self-hosted services.

    5.1 HomeLab Platform Overview

    PlatformHighlightsBest For
    UmbrelBeautiful UI, rich app store, privacy-focusedBitcoin nodes, personal cloud services
    CasaOSLightweight, easy to install, beginner-friendlyFirst-time users, NAS devices
    PortainerFeature-rich, built for power usersComplex multi-container management

    5.2 The Power of One-Click Deployment

    Installing WordPress through tools like Umbrel is remarkably straightforward. These platforms typically provide an app-store-style interface — just find WordPress, click a few buttons, and the installation kicks off automatically. Behind the scenes, the system handles all the heavy lifting: setting up the web server (usually Nginx or Apache), the PHP runtime, and the database (typically MariaDB or MySQL). This one-click or guided installation eliminates the need to manually configure servers, databases, and WordPress files, dramatically lowering the barrier to entry.

    5.3 The Value of Self-Hosted WordPress

    Running your own WordPress instance in a HomeLab gives you something no third-party host can match: complete ownership and control of your data. All your website content and user data lives on your own hardware, which significantly enhances both privacy and security. Beyond that, it provides an outstanding learning and experimentation environment — you’re free to test themes, plugins, and deep-dive into how websites work, without worrying about breaking a live site or racking up bills (aside from hardware and electricity).

    📊 HomeLab vs. Traditional Hosting:

    AspectHomeLab (Self-Hosted)Traditional Cloud Hosting
    Data control✅ Full ownership⚠️ Depends on the provider
    Monthly cost💰 Electricity only💰💰 $10–50+/month
    Learning value🎓 Extremely highRelatively low

    📚 References & Resources: