Migrating WordPress to Docker


From Bare-Metal to Docker

Migrating a legacy WordPress site into Docker isn’t just about moving files; it’s about cleaning up “server rot” and modernizing the stack. Here is the proven workflow for a successful transition.

1. The Extraction (The “Rescue” Phase)

Before touching the new server, extract the essentials from the old host:

  • The Database: Use mysqldump --no-tablespaces to avoid permission errors on modern MariaDB instances.
  • The Content: You only need the /wp-content folder. Let the official WordPress Docker image provide a fresh, clean core for the rest.
  • The Credentials: Capture DB_NAME, DB_USER, DB_PASSWORD, and specifically the $table_prefix from the old wp-config.php.

2. Local Prototyping

Always build the stack on a local machine (like a Fedora laptop) first.

  • Docker Compose: Use a multi-container setup (WordPress + MariaDB).
  • Auto-Import: Place your .sql dump into ./docker-entrypoint-initdb.d/. MariaDB will automatically “rehydrate” the database on the first boot.
  • Networking: Change DB_HOST in wp-config.php from localhost to the name of your database service (e.g., db).
services:
  # --- REVERSE PROXY - for use in testing environment ---
  # uncoment next lines for local testing on NPM (nginx proxy manager)
  # proxy:
  #   image: "jc21/nginx-proxy-manager:latest"
  #   restart: always
  #   ports:
  #     - "8484:80"
  #     - "8443:443"
  #     - "881:81" # Admin Web UI
  #   volumes:
  #     - ./proxy/data:/data
  #     - ./proxy/letsencrypt:/etc/letsencrypt
  #   networks:
  #     - wp_network

  # --- SHARED DATABASE ---
  db:
    image: "mariadb:10.11"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${PANSMAN_DB_NAME}
      MYSQL_USER: ${PANSMAN_DB_USER}
      MYSQL_PASSWORD: ${PANSMAN_DB_PASSWORD}
    volumes:
      - ./mariadb_data:/var/lib/mysql
      # Auto-import your old databases on first run, for testing purposes only 
      # - ./pansman/init.sql:/docker-entrypoint-initdb.d/pansman.sql:ro
      # - ./manosman/init.sql:/docker-entrypoint-initdb.d/manosman.sql:ro

    networks:
      - wp_network

  # --- SITE 1: pan.sman.cloud
  pansman:
    image: wordpress:latest
    restart: always
    ports:
      - "8081:80" # Map host 8081 to container 80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: ${PANSMAN_DB_NAME}
      WORDPRESS_DB_USER: ${PANSMAN_DB_USER}
      WORDPRESS_DB_PASSWORD: ${PANSMAN_DB_PASSWORD}
    volumes:
      - ./pan_sman_cloud:/var/www/html
    networks:
      - wp_network

  # --- SITE 2: mano ` .sman.cloud
  manosman:
    image: wordpress:latest
    restart: always
    ports:
      - "8082:80" # Map host 8082 to container 80 
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_NAME: ${MANOSMAN_DB_NAME}
      WORDPRESS_DB_USER: ${MANOSMAN_DB_USER}
      WORDPRESS_DB_PASSWORD: ${MANOSMAN_DB_PASSWORD}
    volumes:
      - ./mano_sman_cloud:/var/www/html
    networks:
      - wp_network

networks:
  wp_network:
    driver: bridge

volumes:
  db_data:

3. The Production Deployment

When moving to a server that already hosts other containers or Node.js APIs:

  • Reverse Proxy: If port 80/443 is taken by a host-level Nginx, map your WordPress containers to unique local ports (e.g., 8081, 8082).
  • Host Routing: Use the host’s Nginx to proxy_pass traffic to the Docker containers.
  • SSL: Use Certbot on the host machine to handle Let’s Encrypt certificates across all domains.

4. Solving the Permission “Gotcha”

Docker containers run WordPress as the www-data user (UID 33). If your uploaded files are owned by root, WordPress won’t be able to update plugins or upload images.

  • The Fix: Run sudo chown -R 33:33 ./your_site_folder on the host. This ensures the container has full write access immediately.

5. Post-Migration Cleanup

Once live, use WP-CLI (built into the container) to perform a search-and-replace for URLs:

docker compose exec -u www-data <service-name> wp search-replace "http://old-site.test" "https://new-site.cloud" --all-tables

The Technical Stack

Component Technology
Orchestration Docker Compose
Database MariaDB 10.11
PHP/App WordPress (Official Image)
Gateway Nginx (Host-level)
SSL Certbot (Let’s Encrypt)

Final Verdict

Moving to Docker makes your old site portable. If your server fails tomorrow, you can simply move your project folder to a new VPS, run docker compose up -d, and be back online in minutes.

Leave a Comment

Your email address will not be published. Required fields are marked *