# WordPress Operations Guide This document provides WordPress-specific operations, configurations, and troubleshooting for WordPress sites running in Docker behind Traefik on systems-prod-01. ## Docker Configuration Best Practices ### MariaDB Version Selection - **Use MariaDB 10.11** for compatibility with existing WordPress databases - **Avoid MariaDB 10.7** - may cause redo log format errors during restoration - MariaDB 10.11 is the recommended LTS version for WordPress ### SSL/HTTPS Behind Traefik - **Real Simple SSL plugin NOT needed** when behind Traefik - Disable Real Simple SSL to avoid redirect loops - Traefik handles all SSL termination automatically - Add proxy detection configuration to `wp-config.php` (see below) ### Proxy Detection Configuration Add this to your `wp-config.php` before the "That's all, stop editing!" line: ```php // Proxy SSL detection for Traefik if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { $_SERVER['HTTPS'] = 'on'; } ``` ## Using WP-CLI in Docker ### Installing WP-CLI If WP-CLI is not present in your WordPress container: ```bash docker exec CONTAINER_NAME bash -c 'curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && chmod +x wp-cli.phar && mv wp-cli.phar /usr/local/bin/wp' ``` ### Common WP-CLI Commands **List plugins:** ```bash docker exec CONTAINER_NAME wp --allow-root plugin list ``` **Deactivate a plugin:** ```bash docker exec CONTAINER_NAME wp --allow-root plugin deactivate PLUGIN_NAME # Example: Disable Real Simple SSL docker exec CONTAINER_NAME wp --allow-root plugin deactivate really-simple-ssl ``` **Update WordPress core:** ```bash docker exec CONTAINER_NAME wp --allow-root core update ``` **Check WordPress version:** ```bash docker exec CONTAINER_NAME wp --allow-root core version ``` ## Database Operations ### Quick WordPress URL Change When switching between temporary and production domains: ```bash # Generic command (change to production domain) ssh jared@15.204.247.153 "docker exec CONTAINER_DB mysql -u DB_USER -pDB_PASS -e \" USE DATABASE_NAME; UPDATE wp_options SET option_value = 'https://domain.com' WHERE option_name IN ('siteurl', 'home'); \"" # Verify the change ssh jared@15.204.247.153 "docker exec CONTAINER_DB mysql -u DB_USER -pDB_PASS -e \" USE DATABASE_NAME; SELECT option_name, option_value FROM wp_options WHERE option_name IN ('siteurl', 'home'); \"" ``` **Real Example - BestSolarTech:** ```bash # Change to production domain ssh jared@15.204.247.153 "docker exec bestsolartech_db mysql -u wordpress -pBST_wp2024_secure -e \" USE bst_wordpress; UPDATE wp_vuhx_options SET option_value = 'https://bestsolartech.com' WHERE option_name IN ('siteurl', 'home'); \"" # Verify ssh jared@15.204.247.153 "docker exec bestsolartech_db mysql -u wordpress -pBST_wp2024_secure -e \" USE bst_wordpress; SELECT option_name, option_value FROM wp_vuhx_options WHERE option_name IN ('siteurl', 'home'); \"" ``` ### Database Access with Skip Grant Tables If you need to reset database passwords or access without credentials: ```bash # Stop the current database container docker stop SERVICE_db # Start temporary database with skip-grant-tables docker run -d --name temp_db \ -v ~/services/SERVICE/db_data:/var/lib/mysql \ mariadb:10.11 --skip-grant-tables # Access the database (no password needed) docker exec -it temp_db mysql # After making changes, stop temp container and restart normal one docker stop temp_db && docker rm temp_db docker start SERVICE_db ``` ### Database Backup ```bash # Backup WordPress database docker exec CONTAINER_DB mysqldump -u DB_USER -pDB_PASS DATABASE_NAME > backup.sql # Example: Backup BestSolarTech docker exec bestsolartech_db mysqldump -u wordpress -pBST_wp2024_secure bst_wordpress > bestsolartech-backup.sql ``` ### Database Restore ```bash # Restore from backup cat backup.sql | docker exec -i CONTAINER_DB mysql -u DB_USER -pDB_PASS DATABASE_NAME # Example: Restore BestSolarTech cat bestsolartech-backup.sql | docker exec -i bestsolartech_db mysql -u wordpress -pBST_wp2024_secure bst_wordpress ``` ## Troubleshooting ### Redirect Loops **Symptom:** Browser shows "too many redirects" error **Cause:** Real Simple SSL plugin conflicting with Traefik SSL termination **Solution:** Disable Real Simple SSL plugin using WP-CLI (see above) ### Database Connection Errors **Symptom:** "Error establishing database connection" **Cause:** Database container not ready or wrong credentials **Solution:** 1. Check database container is running: `docker ps | grep db` 2. Check database logs: `docker logs CONTAINER_DB` 3. Verify credentials in `wp-config.php` match `docker-compose.yml` ### Mixed Content Warnings **Symptom:** HTTPS page loading HTTP resources **Cause:** Missing proxy detection in `wp-config.php` **Solution:** Add proxy detection code (see above) ## Migration Checklist When migrating a WordPress site to systems-prod-01: - [ ] Backup WordPress files and database from source - [ ] Create service directory: `~/services/SITE_NAME` - [ ] Create `docker-compose.yml` with MariaDB 10.11 - [ ] Add proxy detection code to `wp-config.php` - [ ] Deploy containers with temporary domain - [ ] Test HTTPS on temporary domain - [ ] Disable Real Simple SSL plugin if present - [ ] Update site URLs in database for production domain - [ ] Test all functionality (admin panel, forms, etc.) - [ ] Update DNS for production cutover - [ ] Monitor for 24 hours after migration ## WordPress-Specific Docker Compose Template ```yaml services: wordpress: image: wordpress:6.7-apache restart: unless-stopped environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_NAME: wordpress WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: ${DB_PASSWORD} volumes: - ./wordpress_data:/var/www/html networks: - traefik - internal labels: - "traefik.enable=true" - "traefik.http.routers.SITE.rule=Host(`domain.com`) || Host(`www.domain.com`)" - "traefik.http.routers.SITE.entrypoints=websecure" - "traefik.http.routers.SITE.tls.certresolver=letsencrypt" db: image: mariadb:10.11 restart: unless-stopped environment: MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: ${DB_PASSWORD} MYSQL_RANDOM_ROOT_PASSWORD: '1' volumes: - ./db_data:/var/lib/mysql networks: - internal healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 30s timeout: 10s retries: 5 networks: traefik: external: true internal: external: true ``` ## See Also - [Configuration Reference](./configuration-reference.md) - Docker Compose templates - [Services Inventory](./services-inventory.md) - Current WordPress sites - [CLAUDE.md](../CLAUDE.md) - General server operations