Automated Docker Container Storage Backups

I run several containers in my home lab and the missing piece to my puzzle has been backups. That is to say I have no backup strategy. In my spare time, I've been working out how to backup the container storage and I feel pretty satisfied with my current solution.

Prerequisites

I keep all my container persistent data in a folder on my host machine in the format of /docker/{service-name} so /docker/caddy for instance. Therefore, only this folder should be backed up as it's the hardest to recreate.

Some of my containers use SQLLite or other databases. Copying the persistent data while the container is running could cause corruption. So, stopping and starting the container is necessary.

Finally, I want maximum flexibility so I want to be able to just copy the data in the filesystem.

rclone

Rclone is an application that allows you to mount cloud storage as a logical drive and perform I/O operations against it. Since I am a Microsoft 365 subscriber, I chose to use OneDrive.

Flask Server

I wanted to have a container that would perform actions based on either RESTful command or cronjobs. So I created flask-cron-server which will spin up a Flask server at port 2128. The

From there I was able to create server.py. This is the file that runs the Flask server and is executed on container startup:

It reads in a JSON file called /app/config/containers.json that defines all the containers along with the folder that should be archived and where that archive should be stored.

The call to backup is executed in a separate thread and a 202 Accepted response is sent to the caller to let them know that the command was received, but it is unknown how long it will take.

Finally, the whole thing is driven by a simple JSON

[
    {
        "container_name": "caddy",
        "source_folder": "/source/caddy",
        "destination_folder": "/destination/caddy",
        "retention_days": 7
    },
    {
        "container_name": "freshrss",
        "source_folder": "/source/freshrss",
        "destination_folder": "/destination/freshrss",
        "retention_days": 7
    },
    {
        "container_name": "guacamole",
        "source_folder": "/source/guacamole",
        "destination_folder": "/destination/guacamole",
        "retention_days": 7
    },
    {
        "container_name": "mosquitto",
        "source_folder": "/source/mosquitto",
        "destination_folder": "/destination/mosquitto",
        "retention_days": 7
    },
    {
        "container_name": "ps5-mqtt",
        "source_folder": "/source/ps5-mqtt",
        "destination_folder": "/destination/ps5-mqtt",
        "retention_days": 7
    },
    {
        "container_name": "slash",
        "source_folder": "/source/slash",
        "destination_folder": "/destination/slash",
        "retention_days": 7
    },
    {
        "container_name": "write-freely",
        "source_folder": "/source/writefreely",
        "destination_folder": "/destination/writefreely",
        "retention_days": 7
    },
    {
        "container_name": "homeassistant",
        "source_folder": "/source/ha",
        "destination_folder": "/destination/ha",
        "retention_days": 14
    }
]

Docker Container

The final step was to create the Docker container and stack

services:
  container-backup:
    image: zackwag/flask-container-backup:latest
    container_name: container-backup
    restart: unless-stopped
    ports:
      - 2128:2128
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /docker/container-backup/config:/app/config
      - /docker:/source
      - onedrive-backup:/destination
    environment:
      - PYTHONUNBUFFERED=1
      - TZ=America/New_York
volumes:
  onedrive-backup:
    driver: rclone
    driver_opts:
      remote: onedrive:backup
      allow_other: "true"
      vfs-cache-mode: writes
networks: {}

I'm in the timezone of New York, so you will need to change it to where you live.

Also, I made sure to include /var/run/docker.sock:/var/run/docker.sock:ro so that I could start and stop containers.

Finally, I followed the directions for Docker Volume Plugin . This allowed me to create the volume onedrive-backup that points to the /backup folder in OneDrive. `

Calling Backups

Now that the container is running I can simply call

curl -X POST [IP ADDRESS]:2128/backup

to backup all the containers specified in containers.json or just

curl -X POST {IP ADDRESS}:2128/backup/{container name}

To backup a specific container specified in containers.json.

I have setup automations that daily in Home Assistant, that call the main /backup endpoint to kick off backups.