---
title: "Replication and Failover of a Docker Environment: From Proxmox Container to Cloud — Paolo Ronco"
language: "en"
url: "https://paoloronco.it/en/writing/1377-2"
---

[← writing](/en/writing)

15 July 2025 · 3 min read

# Replication and Failover of a Docker Environment: From Proxmox Container to Cloud

Managing a website on a local server (on-premises) offers many advantages in terms of control and customization, but it also brings an important risk: operational continuity. A home or office server cannot guarantee 100% uptime — just one blackout, a hardware issue, or, as in my case, being away during holidays, and the site could remain offline for hours or days.

* * *

Managing a website on a local server (on-premises) offers many advantages in terms of control and customization, but it also brings an important risk: operational continuity. A home or office server cannot guarantee 100% uptime — just one blackout, a hardware issue, or, as in my case, being away during holidays, and the site could remain offline for hours or days.

For this reason, I decided to implement a **Replication + Cloud Failover** solution, which allows:

-   **Periodic Replication** of WordPress content and database from the local server to the cloud
-   **Automatic (or Semi-Automatic) Failover** in case the local server is offline
-   The ability to manage and update services remotely as well

## How Does My Solution Work?

1.  **Local → Cloud Replication**
    -   Every night, backup scripts for web servers and WordPress database are executed
    -   Files are transferred via `scp` to the cloud server with SSH key authentication
2.  **Cloud Restore**
    -   In the morning, cronjobs on the cloud server run restore scripts: containers are updated and the database is restored
3.  **Failover**
    -   If the local server is online, Cloudflare load balances traffic
    -   If the local server is offline, traffic automatically switches to the cloud server without manual intervention
4.  **Cloud Management**
    -   Portainer for Docker management
    -   Fail2Ban and basic hardening for security

### When the Local Container Is UP:

✅ Handles normal requests

### When the Local Container Is DOWN:

✅ Cloudflare redirects requests to the cloud VM  
✅ The site remains online, services continue to function

## Tutorial:

## OnPremise:

#### a) Backup of Docker Volumes of Web Servers

For each dockerized service (like NGINX or others), I create a backup of the volumes using a temporary Alpine container. At the end, everything is transferred to the cloud via **SCP**.

Example — **Web Server Example**:

```plaintext
#!/bin/bash
DATE=$(date +"%Y%m%d-%H%M")
BACKUP_DIR="/root/backups"
CLOUD_USER="insert_username"
CLOUD_IP="insert_cloud_ip"
SSH_KEY="insert_ssh_key"
REMOTE_PATH="/home/insert_username/webserver_backup_${DATE}.tar.gz"

mkdir -p ${BACKUP_DIR}
cd ${BACKUP_DIR}

# Backup Docker volumes
docker run --rm -v insert_volume_id1:/volume -v $(pwd):/backup alpine sh -c "cd /volume && tar czf /backup/nginx_backup.tar.gz ."
docker run --rm -v insert_volume_id2:/volume -v $(pwd):/backup alpine sh -c "cd /volume && tar czf /backup/html_backup.tar.gz ."

# Create a single archive and transfer to the cloud
tar czf webserver_backup_${DATE}.tar.gz nginx_backup.tar.gz html_backup.tar.gz
scp -i ${SSH_KEY} webserver_backup_${DATE}.tar.gz ${CLOUD_USER}@${CLOUD_IP}:${REMOTE_PATH}
rm -f nginx_backup.tar.gz html_backup.tar.gz
```

* * *

### b) WordPress Database Dump

I execute a SQL dump from the MySQL/MariaDB container and transfer it to the cloud:

```plaintext
#!/bin/bash
DATE=$(date +"%Y%m%d-%H%M")
LOCAL_DUMP="/root/wordpress_db_backup_${DATE}.sql"
CLOUD_USER="insert_username"
CLOUD_IP="insert_cloud_ip"
SSH_KEY="insert_ssh_key"
DB_USER="insert_db_user"
DB_PASS="insert_db_password"
DB_NAME="insert_db_name"

docker exec insert_container_name_db mysqldump --no-tablespaces -u ${DB_USER} -p"${DB_PASS}" ${DB_NAME} > ${LOCAL_DUMP}
scp -i ${SSH_KEY} ${LOCAL_DUMP} ${CLOUD_USER}@${CLOUD_IP}:/home/insert_username/
```

* * *

### c) Automation with Cronjob

Example of crontab:

`0 3 * * * /root/scripts/replica_webserver.sh 0 4 * * * /root/scripts/replica_wordpress.sh`

## Cloud:

### a) Restore of Web Server Volumes

On the cloud, I unpack the received backups and restore them to Docker volumes:

```plaintext
#!/bin/bash
BACKUP_FILE=$(ls -t /home/insert_username/webserver_backup_*.tar.gz | head -n 1)
RESTORE_DIR="/root/restore_temp"
mkdir -p ${RESTORE_DIR}
tar xzf ${BACKUP_FILE} -C ${RESTORE_DIR}
docker run --rm -v insert_volume_id1:/data -v ${RESTORE_DIR}:/backup alpine sh -c "rm -rf /data/* && tar xzf /backup/nginx_backup.tar.gz -C /data"
docker run --rm -v insert_volume_id2:/data -v ${RESTORE_DIR}:/backup alpine sh -c "rm -rf /data/* && tar xzf /backup/html_backup.tar.gz -C /data"
rm -rf ${RESTORE_DIR}
```

* * *

### b) Restore of WordPress Database

```plaintext
#!/bin/bash
DUMP_PATH=$(ls -t /home/insert_username/wordpress_db_backup_*.sql | head -n 1)
CONTAINER_DB="insert_container_name_db"
DB_USER="insert_db_user"
DB_PASS="insert_db_password"
DB_NAME="insert_db_name"

docker cp "$DUMP_PATH" "$CONTAINER_DB:/wordpress_db_backup.sql"
docker exec -i "$CONTAINER_DB" mysql -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" -e "SET FOREIGN_KEY_CHECKS=0;DROP TABLE IF EXISTS wp_commentmeta, wp_comments, wp_links, wp_options, wp_postmeta, wp_posts, wp_term_relationships, wp_terms, wp_term_taxonomy, wp_usermeta, wp_users;SET FOREIGN_KEY_CHECKS=1;"
docker exec -i "$CONTAINER_DB" sh -c "mysql -u $DB_USER -p\"$DB_PASS\" $DB_NAME < /wordpress_db_backup.sql"
```

* * *

### c) Automation with Cronjob on Cloud

Example of crontab:

```plaintext
0 5 * * * /root/scripts/restore_webserver.sh
0 6 * * * /root/scripts/restore_wordpress.sh
```

## Cloudflare:

On both machines (Proxmox and Cloud), I activate the Cloudflare tunnel:

```plaintext
cloudflared service install insert_tunnel_token
```

## Cloudflare automatically manages traffic based on the availability of the on-premises container or the cloud VM.

On this page

-   [How Does My Solution Work?](#how-does-my-solution-work)
-   [Tutorial:](#tutorial)
-   [OnPremise:](#onpremise)
-   [Cloud:](#cloud)
-   [Cloudflare:](#cloudflare)
-   [Cloudflare automatically manages traffic based on the availability of the on-premises container or the cloud VM.](#cloudflare-automatically-manages-traffic-based-on-the-availability-of-the-on-premises-container-or-the-cloud-vm)
