Tag: Docker

  • One-Pyeong Server — Running WordPress with Docker

    One-Pyeong Server — Running WordPress with Docker

    One-Pyeong Server — Running WordPress with Docker

    Self-hosting deployment part 2. Infrastructure skeleton in 30 minutes.


    TL;DR

    • Launch WordPress + MariaDB + Redis three containers with a single docker-compose.yml file.
    • Only expose host port 127.0.0.1:8090. External routing handled by Cloudflare Tunnel (part 9).
    • Use Redis as an Object Cache for database query caching. Free and improves page response time by 30%.
    • Five traps: Korean file name SCP, healthcheck --connect, persisted password in named volume, WP_REDIS_HOST=redis, WORDPRESS_CONFIG_EXTRA X-Forwarded-Proto.

    1. Why Docker — Host Installation vs Container

    Comparison Direct Host (apt install) Docker Compose ⭐
    Installation Simplicity apt install apache2 php mariadb-server, etc. docker compose up -d
    Dependency Conflicts Conflicts like PHP 8.1 vs 8.3 arise Container Isolation
    Mini-PC OS Reinstallation Start from scratch Just compose.yml + data backup
    Updates System package manager (complexity) docker compose pull && up -d
    Coexistence of Different Containers (WP, Umami, cloudflared) Difficult Natural

    If the mini-PC is already running other services with Docker, the answer is Docker.


    2. Structure on a Page

    

    The three containers communicate within the same Docker network using hostnames (db, redis). Only the host 127.0.0.1:8090 is exposed externally.


    3. Key docker-compose.yml

    services:
      wordpress:
        image: wordpress:php8.3-apache
        restart: unless-stopped
        depends_on:
          db:
            condition: service_healthy
        environment:
          WORDPRESS_DB_HOST: db
          WORDPRESS_DB_USER: wp
          WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
          WORDPRESS_DB_NAME: wp
          WORDPRESS_CONFIG_EXTRA: |
            if (!empty($$_SERVER['HTTP_X_FORWARDED_PROTO']) && $$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
              $$_SERVER['HTTPS'] = 'on';
            }
        volumes:
          - wordpress_data:/var/www/html
        ports:
          - "127.0.0.1:8090:80"
    
      db:
        image: mariadb:11
        restart: unless-stopped
        environment:
          MARIADB_DATABASE: wp
          MARIADB_USER: wp
          MARIADB_PASSWORD: ${DB_PASSWORD}
          MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
        volumes:
          - wordpress_db_data:/var/lib/mysql
        healthcheck:
          test: ["CMD", "healthcheck.sh", "--connect"]
          interval: 10s
          timeout: 5s
          retries: 6
          start_period: 30s
    
      redis:
        image: redis:7-alpine
        restart: unless-stopped
        volumes:
          - wordpress_redis_data:/data
    
    volumes:
      wordpress_data:
      wordpress_db_data:
      wordpress_redis_data:
    

    Separate .env:

    DB_PASSWORD=<openssl rand -hex 24>
    DB_ROOT_PASSWORD=<openssl rand -hex 24>
    

    chmod 600 .env. Do not include in git.


    4. Start + Verification

    cd /home/user/wordpress
    docker compose up -d
    docker compose ps
    curl -I http://localhost:8090
    

    If the response is HTTP/1.1 302 (redirecting to WordPress installation page), it is successful.

    Access http://localhost:8090 in a browser → 5-minute wizard → create admin account (do not use admin as the name, choose your own to avoid initial brute force).


    5. Activate Redis Object Cache

    After installing the redis-cache plugin:

    wp config set WP_REDIS_HOST redis --allow-root
    wp config set WP_REDIS_PORT 6379 --allow-root
    wp config set WP_REDIS_PREFIX byeolgorae: --allow-root
    wp config set WP_CACHE true --raw --allow-root
    wp redis enable --allow-root
    

    WP_REDIS_HOST=redis is key. It is the Docker hostname. Using 127.0.0.1 results in failure as it only looks within its own container.


    6. Traps — Real Experiences Faced

    Trap 1. Korean and Box Characters in File SCP

    While writing docker-compose.yml with minipc_write_file, the SSH escape broke due to a combination of Korean comments and special characters (bash: -c: unexpected end of file while looking for matching quotation mark).

    → Write plain text in c:\tmp\ on Windows → scp → mini-PC /tmp/ → circumvent by sudo cp.

    Trap 2. mariadb healthcheck unsupported --innodb-initialized

    Following the web guide with ["CMD", "healthcheck.sh", "--connect", "--innodb-initialized"] led to an unknown option error.

    → Use only --connect + extend start_period: 30s.

    Trap 3. “Database Error” after changing .env password

    After changing the DB password and running docker compose up -d, a 500 error appeared. Reason: The old password was retained in the MariaDB DB file stored in the named volume. The new password failed authentication.

    → Run docker compose down -v (the -v removes volumes) → restart with up -d. Do not do this if operational data exists; only at initial setup stage.

    Trap 4. Redis Connection refused 127.0.0.1:6379

    After setting WP_REDIS_HOST=127.0.0.1, it resulted in the container trying to access itself. Redis is in another container.

    → Set WP_REDIS_HOST=redis (Docker network hostname).

    Trap 5. HTTPS Reverse Proxy Infinite Redirect

    With Cloudflare Tunnel in front, it forwards HTTP to the mini-PC after terminating HTTPS. WordPress identifies itself as HTTP → redirects saying it must go to “HTTPS” → infinite loop.

    → Handle processing for X-Forwarded-Proto in WORDPRESS_CONFIG_EXTRA (see above yml).


    FAQ

    Q. How much memory is used?
    WP (php-fpm or apache) ~200MB, MariaDB ~150MB, Redis ~30MB. Total ~400MB. Plenty of room with 8GB RAM on the mini-PC.

    Q. What about disk space?
    Image combined ~1GB. DB size varies (100 articles + images = ~500MB).

    Q. Can I use PHP 8.2 instead of 8.3?
    WordPress 6.x supports both 8.2 and 8.3. 8.3 is faster (JIT stabilization). It is recommended to use the latest.

    Q. What if I use mysql instead of mariadb?
    mariadb is compatible with mysql + free licensing + no Oracle dependency. Both will work. mariadb is recommended for single-user operations.

    Q. How to backup?
    Details covered in part 4 (backup, cache, images). Daily cron at 03:00 runs mariadb-dump → gzip → stored in Obsidian Vault.


    Next Part Preview

    Part 3 — For AI to Read: robots.txt and llms.txt. Specify allowances for 14 types of AI crawlers instead of discrimination against bots. Content exposure strategy for the GEO era.


    Summary in One Line

    Launching three containers (WordPress, MariaDB, Redis) through a single docker-compose.yml file in 30 minutes. Only exposing host 127.0.0.1:8090; external routing delegated to Cloudflare Tunnel. Traps include Korean SCP, healthcheck issues, password persistence in volumes, Redis host misconfiguration, and X-Forwarded-Proto problems.