# Copias de volúmenes Docker en GitHub: recuperación de bajo coste para 1Panel

> Canonical: https://vxi.me/es/posts/docker-volume-backup-github-sync/
> Locale: es-es
> Published: 2026-03-25
> Updated: 2026-03-25
> Markdown: https://vxi.me/es/posts/docker-volume-backup-github-sync.md

Cuando ejecutas servicios en Docker, lo molesto rara vez es volver a arrancar los contenedores. El verdadero problema aparece cuando falla un disco, hay que reconstruir una máquina o desaparece la configuración del panel y descubres que los datos de los volúmenes nunca se guardaron de una forma que realmente puedas restaurar.

Por eso terminé usando un modelo de backup bastante sencillo para mi servidor 1Panel: archivar los volúmenes Docker todos los días, conservar localmente los últimos siete días y sincronizar los archivos comprimidos con un repositorio privado de GitHub mediante Git LFS. No es el sistema de backup Docker más avanzado que podrías construir, pero sí una de las configuraciones más simples que todavía ofrece una ruta real de recuperación.

Si buscas backup de volúmenes Docker a GitHub, backup automático de Docker o un workflow práctico de backup para 1Panel, este es exactamente el espacio del problema.

## Por qué mantuve el modelo de backup deliberadamente pequeño

La tentación siempre es saltar directamente a una pila más elaborada: object storage, políticas de retención, replicación off-site o incluso un producto dedicado de backup.

Eso puede ser lo correcto más adelante. Pero en un servidor personal o una máquina de equipo pequeña, la primera mejora real suele ser mucho más sencilla:

- hacer backup todos los días
- conservar copias recientes en la máquina para recuperación rápida
- mantener otra copia fuera de la máquina

Esa es toda la idea del workflow:

```text
Docker volumes / 1Panel config
          ↓
     docker-sync
          ↓
Create backup_<timestamp>
          ↓
Delete local backups older than 7 days
          ↓
Copy archives into Git working tree
          ↓
Track .tar.gz with Git LFS
          ↓
Push to a private GitHub repository
```

Es suficientemente ligero para seguir ejecutándose. Eso importa más que tener un sistema teóricamente perfecto que nunca se termina.

## Configura primero la confianza SSH con GitHub

Ni siquiera tocaría la parte de automatización hasta que el servidor pueda hacer push a GitHub correctamente.

Ejecuta esto en el servidor:

```bash
ssh-keygen -t ed25519 -C "your-email@example.com"
cat ~/.ssh/id_ed25519.pub
ssh -T git@github.com
```

El primer comando crea la clave, el segundo permite copiar la parte pública a las SSH keys de GitHub y el tercero es el más importante: una prueba manual de conexión.

Este paso ahorra tiempo después. Muchos jobs programados de backup no fallan porque la lógica de copia sea incorrecta, sino porque SSH nunca quedó completamente configurado.

También prefiero utilizar un repositorio privado dedicado al backup en lugar de mezclarlo con un repositorio de aplicación:

```text
git@github.com:kuangyongjie/my-docker-backup.git
```

Privado es el valor por defecto evidente aquí.

## Lo envolví todo en un único comando `docker-sync`

Intento evitar que la lógica operativa importante quede enterrada en alguna carpeta aleatoria de scripts.

Si este workflow va a durar meses o años, el punto de entrada tiene que ser estable. Por eso lo convertí en un comando del sistema:

```text
/usr/local/bin/docker-sync
```

Así el mismo comando sirve tanto para ejecuciones manuales como para tareas programadas desde 1Panel.

Este es el script principal:

```bash
cat << 'EOF' > /usr/local/bin/docker-sync
#!/bin/bash

GITHUB_REPO="git@github.com:kuangyongjie/my-docker-backup.git"
GIT_USER_NAME="kuangyongjie"
GIT_USER_EMAIL="admin@local.host"
LOCAL_SYNC_PATH="/root/github_sync_space"

if ! command -v git-lfs &> /dev/null; then
    apt-get update && apt-get install -y git git-lfs && git lfs install
fi

if [ ! -d "$LOCAL_SYNC_PATH/.git" ]; then
    echo "--- 第一次运行：初始化同步目录 ---"
    rm -rf "$LOCAL_SYNC_PATH"
    GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" git clone "$GITHUB_REPO" "$LOCAL_SYNC_PATH"
    cd "$LOCAL_SYNC_PATH"
    git lfs track "*.tar.gz" && git add .gitattributes
    git config user.name "$GIT_USER_NAME" && git config user.email "$GIT_USER_EMAIL"
    git commit -m "Init LFS"
else
    cd "$LOCAL_SYNC_PATH" && git pull origin main --rebase
fi

TAG=$(date +%Y%m%d_%H%M%S)
DIR="/root/docker_backups/backup_$TAG"
mkdir -p "$DIR"

echo "--- 正在抓取本地数据 ---"
for vol in $(docker volume ls -q); do
    tar -zcf "$DIR/vol_$vol.tar.gz" -C $(docker volume inspect $vol --format '{{.Mountpoint}}') .
done

tar -zcf "$DIR/1panel_apps_config.tar.gz" /opt/1panel/apps

echo "--- 正在清理本地 7 天前的旧备份 ---"
find /root/docker_backups/ -maxdepth 1 -name "backup_*" -type d -mtime +7 -exec rm -rf {} +

echo "--- 正在同步到 GitHub ---"
cp "$DIR"/*.tar.gz "$LOCAL_SYNC_PATH/"
cd "$LOCAL_SYNC_PATH"
git add .
git commit -m "Auto Backup $TAG"
git push origin main
echo "--- 完美！所有任务已完成 ---"
EOF

chmod +x /usr/local/bin/docker-sync
```

## Qué hace realmente el script

El script parece más largo de lo que es.

En la práctica hace seis cosas:

1. instala `git-lfs` si falta
2. clona e inicializa el repositorio de backup en la primera ejecución
3. recorre los volúmenes Docker y archiva cada uno
4. archiva el directorio de configuración de aplicaciones de 1Panel
5. elimina backups locales con más de siete días
6. copia los nuevos archivos al repo de sincronización, hace commit y push

Esa sencillez es exactamente lo que me gusta del sistema. Las piezas móviles siguen siendo fáciles de entender.

Hay dos puntos que vigilaría:

- `git commit` falla si no hay nada nuevo que commitear
- `docker volume ls -q` copia todos los volúmenes, por lo que conviene saber qué hay en la máquina

Ninguno es difícil de resolver, pero ambos importan cuando el proceso empieza a ejecutarse sin supervisión.

## En 1Panel, mantén mínima la tarea programada

No pegaría todo el script de backup dentro de la tarea de 1Panel.

Es más limpio conseguir primero que el comando shell sea sólido y después dejar que 1Panel solo se encargue de programarlo:

1. ejecuta `docker-sync` manualmente una vez y comprueba que funciona
2. crea una tarea programada en 1Panel
3. elige Shell Script
4. ejecútala una vez al día, por ejemplo a las `03:00`
5. usa esto como cuerpo completo:

```bash
docker-sync
```

Esto facilita mucho el debugging. Si el job falla después, puedes entrar por SSH y ejecutar exactamente el mismo comando en lugar de leer un script largo dentro de la interfaz del panel.

## La recuperación importa más que la elegancia del backup

Muchas guías terminan en “los archivos ya están guardados en algún sitio”. Eso no es lo importante cuando algo se rompe de verdad.

La prueba real es el flujo de recuperación:

1. clonar el repositorio de backup en un servidor nuevo
2. localizar el archivo correspondiente al momento correcto
3. extraer el `vol_xxx.tar.gz` relevante
4. recrear el volumen Docker objetivo
5. copiar los datos al mountpoint de ese volumen
6. recrear el contenedor y montar de nuevo el volumen

Los comandos exactos dependen del servicio que estés restaurando, pero el camino es aproximadamente el mismo.

La fricción real normalmente no está en extraer archivos. Está en recordar qué volumen pertenece a qué servicio cuando tienes prisa. Si adoptas este workflow, mantén claros los nombres de volúmenes y su mapeo con servicios.

## Los pocos comandos que realmente utilizo

La mayor parte del tiempo solo necesito estas comprobaciones:

| Necesidad | Comando |
| --- | --- |
| Ejecutar un backup ahora | `docker-sync` |
| Comprobar uso de disco local | `du -sh /root/docker_backups` |
| Ver carpetas de backup | `ls -lh /root/docker_backups` |
| Revisar historial reciente | `cd /root/github_sync_space && git log -n 5` |
| Cambiar días de retención | `nano /usr/local/bin/docker-sync` |

Si quieres otra ventana de retención, basta con cambiar la línea `find ... -mtime +7`. No hay que rediseñar todo el workflow.

## Dónde es más probable que falle

En la práctica, las partes frágiles no suelen ser los comandos `tar`.

Suelen ser cosas como:

- confianza SSH incompleta entre el servidor y GitHub
- `git-lfs` no instalado o no inicializado
- permisos incorrectos en el directorio local de sincronización
- ejecuciones programadas sin cambios, por lo que `git commit` devuelve un código distinto de cero

También hay una cuestión de escala que no conviene ignorar. Un repositorio privado de GitHub funciona bien para configuraciones, cantidades moderadas de datos de volúmenes Docker y backups habituales de aplicaciones. Cuando el tamaño de los archivos empieza a crecer rápidamente, object storage o un destino de backup más especializado probablemente tenga más sentido.

Por eso conviene describir correctamente este sistema: no es la forma final de disaster recovery. Es una manera práctica de dar a un servidor Docker y 1Panel un hábito de backup automático real sin introducir de golpe mucha infraestructura adicional.
