5.4 KiB
Migration: hair-select.ch from 199 to 208
Migration from nginx-proxy (199.241.137.4) to Traefik (208.87.129.133)
Date: 2025-12-10 Domain: hair-select.ch Table Prefix: wp_393247_
Source (199.241.137.4)
- Containers:
ch_hair_select_wp,ch_hair_select_wp_db - Database:
ch_hair_select_wp_db_name - WordPress files:
/home/g/Documents/workspace/wordpress_sites/ch_hair_select/wordpress_files - Setup: nginx-proxy + letsencrypt companion, MySQL 5.7, bind mounts
Target (208.87.129.133)
- Containers:
php-wp-ch_hair_select_wp,php-wp-ch_hair_select_wp_db, etc. - Database:
php-wp-ch_hair_select-db_name - Setup: Traefik, MariaDB 11, Docker volumes
- Deployment: Drone CI from Gitea
Migration Steps
1. Project Setup
# Copy template from danipages project
cp -r php-wp-com_danipages php-wp-ch_hair_select
cd php-wp-ch_hair_select
# Clean up
rm -rf .git MIGRATION.md
# Initialize git
git init
git add -A
git commit -m "Initial commit from template"
2. Generate Secure Credentials
# Generate .env.prod with secure passwords and WordPress salts
./generate-env-secrets.sh php-wp-ch_hair_select hair-select.ch wp_393247_
This script:
- Generates random passwords using
openssl rand -base64 32 - Fetches WordPress salts from https://api.wordpress.org/secret-key/1.1/salt/
- Updates .env.prod with all required values
3. Hydrate Vault
# Run from project directory
vault-new-project --env_file .env.prod --name php-wp-ch_hair_select --inventory_hostname sn48
4. Create Gitea Repository
- Create repo at https://gitea.sn48.zivili.ch:
php-wp-ch_hair_select - Add remote:
git remote add origin ssh://git@gitea.sn48.zivili.ch:222/gbili/php-wp-ch_hair_select.git
5. Activate in Drone
- Go to https://drone.sn48.zivili.ch
- Find
php-wp-ch_hair_selectrepository - Click "Activate" to enable builds
6. Push to Trigger Deployment
git push -u origin master
Wait for Drone to deploy containers on 208.
7. Export Data from Source (199)
# Export database (84MB)
ssh g@199.241.137.4 "docker exec ch_hair_select_wp_db sh -c 'mysqldump -u root -p\$MYSQL_ROOT_PASSWORD ch_hair_select_wp_db_name' > /tmp/hair_select_db.sql"
# Export wp-content via docker (184MB) - avoids permission issues with Wordfence
ssh g@199.241.137.4 "docker exec ch_hair_select_wp tar -czf /tmp/wp_content.tar.gz -C /var/www/html wp-content && docker cp ch_hair_select_wp:/tmp/wp_content.tar.gz /tmp/hair_select_wp_content.tar.gz"
8. Transfer to Target (208)
scp g@199.241.137.4:/tmp/hair_select_db.sql g@199.241.137.4:/tmp/hair_select_wp_content.tar.gz g@208.87.129.133:/tmp/
9. Import Data on Target (208)
# Copy SQL into container and import
ssh g@208.87.129.133 "docker cp /tmp/hair_select_db.sql php-wp-ch_hair_select_wp_db:/tmp/hair_select_db.sql && docker exec php-wp-ch_hair_select_wp_db sh -c 'mariadb -u root -p\"\$MYSQL_ROOT_PASSWORD\" \"\$MYSQL_DATABASE\" < /tmp/hair_select_db.sql'"
# Extract wp-content to volume
ssh g@208.87.129.133 "docker run --rm -v php-wp-ch_hair_select_wp-data:/data -v /tmp:/backup alpine sh -c 'cd /data && tar -xzf /backup/hair_select_wp_content.tar.gz --strip-components=1'"
10. Update DNS
Update A record for hair-select.ch to point to 208.87.129.133
Verify:
dig hair-select.ch +short
# Should return: 208.87.129.133
11. Restart Container for SSL
ssh g@208.87.129.133 "docker restart php-wp-ch_hair_select_wp"
Verify SSL certificate:
echo | openssl s_client -connect hair-select.ch:443 -servername hair-select.ch 2>/dev/null | openssl x509 -noout -issuer -subject -dates
12. Verify Site
# Test internally (before DNS propagates)
ssh g@208.87.129.133 "curl -sL -H 'Host: hair-select.ch' http://localhost | head -50"
# Test externally
curl -sI https://hair-select.ch
13. Decommission Old Containers
ssh g@199.241.137.4 "docker stop ch_hair_select_wp ch_hair_select_wp_db && docker rm ch_hair_select_wp ch_hair_select_wp_db"
Key Differences: 199 vs 208
| Aspect | 199 (Source) | 208 (Target) |
|---|---|---|
| Reverse proxy | nginx-proxy + letsencrypt companion | Traefik |
| Database | MySQL 5.7 | MariaDB 11 |
| DB command | mysql |
mariadb |
| Volume type | Bind mounts (entire /var/www/html) | Docker volumes (wp-content only) |
| SSL config | LETSENCRYPT_HOST env var | Traefik labels (certresolver) |
| Deployment | Manual docker-compose | Drone CI |
Troubleshooting
Database import fails with "Access denied"
The -i flag with docker exec doesn't pass stdin properly when piping from outside. Solution: Copy SQL file into container first, then import from inside.
wp-content export permission denied
Wordfence creates files with restricted permissions. Use docker exec to tar from inside the container, then docker cp to extract.
SSL shows self-signed cert
Deploy AFTER DNS points to new server. If deployed before, restart the container after DNS propagates:
docker restart php-wp-ch_hair_select_wp
Site redirects to /wp-admin/install.php
TABLE_PREFIX mismatch. Check source database tables:
docker exec <db_container> mariadb -u root -p<pass> <db_name> -e 'SHOW TABLES;'
Update TABLE_PREFIX in .env.prod, re-run vault-new-project, redeploy.
Data Sizes
- Database dump: 84MB
- wp-content archive: 184MB
- Divi theme version: 4.27.5 (PHP 8.3 compatible)