server-ovh-prod-01-main/configs/wordpress-docker-migration-...

271 lines
8.2 KiB
Markdown
Raw Normal View History

# WordPress Docker Migration Guide - Lessons Learned
**Last Updated**: 2025-09-19
**Based on**: BestSolarTech.com migration experience
## Critical Gotchas & Solutions
### 1. Variable Escaping in Docker Compose Environment Variables
**Problem**: `$_SERVER` variables in WORDPRESS_CONFIG_EXTRA are interpreted as shell variables, causing warnings and broken configuration.
**Symptom**:
```
level=warning msg="The \"_SERVER\" variable is not set. Defaulting to a blank string."
```
**Wrong**:
```yaml
WORDPRESS_CONFIG_EXTRA: |
if (isset(\$_SERVER['HTTP_X_FORWARDED_PROTO'])) {
```
**Correct**:
```yaml
WORDPRESS_CONFIG_EXTRA: |
if (isset($$_SERVER['HTTP_X_FORWARDED_PROTO'])) {
```
**Solution**: Use `$$` to escape dollar signs in Docker Compose environment variables.
### 2. Traefik v3 Host Rule Syntax
**Problem**: Multiple hosts in Traefik labels don't work with comma separation.
**Wrong**:
```yaml
- "traefik.http.routers.site.rule=Host(`domain1.com`, `domain2.com`, `domain3.com`)"
```
**Correct**:
```yaml
- "traefik.http.routers.site.rule=Host(`domain1.com`) || Host(`domain2.com`) || Host(`domain3.com`)"
```
**Solution**: Use `||` (OR operator) to separate multiple hosts in Traefik v3.
### 3. MariaDB Version Compatibility
**Problem**: MariaDB version mismatches cause redo log format errors.
**Error**:
```
[ERROR] InnoDB: Unsupported redo log format. The redo log was created with MariaDB 10.11.14.
```
**Solution**:
- Check the original database version before migration
- Use the same or newer MariaDB version
- MariaDB 10.11 is a safe choice for most WordPress sites
### 4. Real Simple SSL Plugin Behind Traefik
**Problem**: Real Simple SSL plugin causes redirect loops when WordPress is behind Traefik reverse proxy.
**Solution**:
1. Disable the plugin BEFORE migration:
```bash
# Via database (serialized array - be careful!)
UPDATE wp_options SET option_value = 'a:0:{}' WHERE option_name = 'active_plugins';
# OR use WP-CLI after migration
docker exec CONTAINER wp --allow-root plugin deactivate really-simple-ssl
```
2. Add proxy detection to wp-config.php:
```php
/* Handle SSL behind reverse proxy */
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
$_SERVER['SERVER_PORT'] = 443;
}
define('FORCE_SSL_ADMIN', true);
```
### 5. WordPress URL Management Strategy
**Problem**: Changing URLs too early causes redirect loops and access issues.
**Best Practice**:
1. Start with a temporary domain (e.g., temp.example.com)
2. Test everything thoroughly
3. Only update to production domain after confirming everything works
4. Keep both domains in Traefik configuration during transition
**Database URL Update**:
```sql
UPDATE wp_options SET option_value = 'https://yourdomain.com'
WHERE option_name IN ('siteurl', 'home');
```
### 6. Database Password Reset for Existing Databases
**Problem**: Existing database has unknown root password.
**Solution** - Use skip-grant-tables:
```bash
# Start MariaDB with skip-grant-tables
docker run -d --name temp_db -v ~/services/SERVICE/db_data:/var/lib/mysql mariadb:10.11 --skip-grant-tables
# Reset passwords
docker exec temp_db mysql -e "
USE mysql;
FLUSH PRIVILEGES;
ALTER USER 'root'@'%' IDENTIFIED BY 'new_password';
CREATE USER IF NOT EXISTS 'wordpress'@'%' IDENTIFIED BY 'wp_password';
GRANT ALL PRIVILEGES ON dbname.* TO 'wordpress'@'%';
FLUSH PRIVILEGES;
"
# Stop temp container and start normal service
docker stop temp_db && docker rm temp_db
```
## Complete Migration Checklist
### Pre-Migration
- [ ] Document current WordPress version, PHP version, active theme
- [ ] List all active plugins (especially security/SSL plugins)
- [ ] Check database size and MariaDB version
- [ ] Note table prefix (often not default `wp_`)
- [ ] Create full backup of files and database
### File Migration
- [ ] Copy WordPress files preserving structure:
```bash
sudo cp -r /backup/wordpress ~/services/site/wordpress
sudo chown -R www-data:www-data ~/services/site/wordpress
```
- [ ] Copy database files:
```bash
sudo cp -r /backup/mysql ~/services/site/db_data
sudo chown -R 999:999 ~/services/site/db_data
```
- [ ] Copy/create uploads.ini for PHP settings
### Docker Compose Configuration
- [ ] Use correct MariaDB version (10.11 recommended)
- [ ] Escape `$` signs as `$$` in environment variables
- [ ] Use `||` for multiple hosts in Traefik rules
- [ ] Include both internal and traefik networks
- [ ] Set health check for database
- [ ] Configure volumes for persistence
### WordPress Configuration
- [ ] Update wp-config.php database host to 'mariadb:3306'
- [ ] Add proxy SSL detection code
- [ ] Remove hardcoded URLs (use $_SERVER['HTTP_HOST'])
- [ ] Disable SSL plugins before starting
### Testing Phase
- [ ] Start with temporary domain
- [ ] Test database connectivity
- [ ] Verify no redirect loops
- [ ] Check admin panel access
- [ ] Test file uploads
- [ ] Verify all plugins working
### Production Cutover
- [ ] Update DNS records to new server
- [ ] Update database URLs to production domain
- [ ] Test SSL certificate generation
- [ ] Monitor logs for errors
- [ ] Have rollback plan ready
## Sample Docker Compose Template
```yaml
services:
wordpress:
image: wordpress:6.7-php8.3-apache
container_name: ${SITE_NAME}_wp
restart: unless-stopped
environment:
WORDPRESS_DB_HOST: mariadb:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
WORDPRESS_DB_NAME: ${DB_NAME}
WORDPRESS_TABLE_PREFIX: ${TABLE_PREFIX:-wp_}
WORDPRESS_CONFIG_EXTRA: |
/* Handle SSL behind reverse proxy */
if (isset($$_SERVER['HTTP_X_FORWARDED_PROTO']) && $$_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$$_SERVER['HTTPS'] = 'on';
$$_SERVER['SERVER_PORT'] = 443;
}
define('FORCE_SSL_ADMIN', true);
define('WP_MEMORY_LIMIT', '256M');
volumes:
- ./wordpress:/var/www/html
- ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini:ro
networks:
- internal
- traefik
depends_on:
mariadb:
condition: service_healthy
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.${SITE_NAME}.rule=Host(`${DOMAIN}`)"
- "traefik.http.routers.${SITE_NAME}.entrypoints=web"
- "traefik.http.routers.${SITE_NAME}.middlewares=redirect-to-https@file"
- "traefik.http.routers.${SITE_NAME}-secure.rule=Host(`${DOMAIN}`)"
- "traefik.http.routers.${SITE_NAME}-secure.entrypoints=websecure"
- "traefik.http.routers.${SITE_NAME}-secure.tls=true"
- "traefik.http.routers.${SITE_NAME}-secure.tls.certresolver=letsencrypt"
- "traefik.http.services.${SITE_NAME}.loadbalancer.server.port=80"
mariadb:
image: mariadb:10.11
container_name: ${SITE_NAME}_db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}
MYSQL_USER: wordpress
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- ./db_data:/var/lib/mysql
networks:
- internal
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 60s
networks:
traefik:
external: true
internal:
external: true
```
## WP-CLI Installation
Install WP-CLI in WordPress container for management:
```bash
docker exec CONTAINER 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'
# Use with --allow-root flag
docker exec CONTAINER wp --allow-root plugin list
docker exec CONTAINER wp --allow-root cache flush
```
## Quick Troubleshooting
| Problem | Solution |
|---------|----------|
| 404 errors from Traefik | Restart Traefik: `docker restart traefik` |
| Redirect loops | Check database URLs match expected domain |
| SSL certificate errors | Check DNS points to server, wait for Let's Encrypt |
| Database connection failed | Verify credentials, check container network |
| Plugin conflicts | Disable via database or WP-CLI |
## Important Notes
- Always test on a temporary domain first
- Keep backups before making changes
- Document all credentials securely
- Monitor logs during migration
- Have a rollback plan ready