Image that handles database backup. It detects container with a database and handles its backup into a remote location.
Find a file
Adam Štrauch 5698b278b5
All checks were successful
Build a dev image / build (push) Successful in 15s
Infinity loop
2025-10-18 19:31:52 +02:00
.gitea/workflows Initial commit 2025-10-18 01:48:43 +02:00
backup.sh Infinity loop 2025-10-18 19:31:52 +02:00
backup_local.sh Add local snapshot command 2025-10-18 02:08:37 +02:00
backup_restic.sh Add local snapshot command 2025-10-18 02:08:37 +02:00
Dockerfile Add local snapshot command 2025-10-18 02:08:37 +02:00
README.md Infinity loop 2025-10-18 19:31:52 +02:00
Taskfile.yml Add local snapshot command 2025-10-18 02:08:37 +02:00

Database Backup Container

A lightweight Alpine-based Docker container for backing up MariaDB and PostgreSQL databases with support for both local snapshots and Restic backups.

Overview

This container automatically detects the database type (MariaDB or PostgreSQL) in a target container and creates backups using either local file storage with compression or Restic repositories. It supports backing up databases from other Docker containers by executing dump commands inside them.

Features

  • Multi-database support: Automatically detects and backs up MariaDB or PostgreSQL databases
  • Dual backup methods: Choose between local compressed files or Restic repositories
  • Local snapshots: Create compressed (zstd) local backup files with timestamps
  • Restic integration: Uses Restic for efficient, encrypted, and deduplicated backups
  • Docker-in-Docker: Can access and backup databases from other containers
  • Notification support: Optional webhook notifications when backups complete
  • Lightweight: Based on Alpine Linux for minimal footprint

Prerequisites

  • Docker with socket access (/var/run/docker.sock)
  • Target container with either MariaDB or PostgreSQL client tools
  • For Restic backups: Restic repository (local, S3, B2, etc.)
  • For local backups: Mounted volume for backup storage

Environment Variables

Required (All Methods)

Variable Description
CONTAINER Name of the Docker container where the database is running

For Restic Backups

Variable Description
RESTIC_PASSWORD Password for the Restic repository
RESTIC_REPOSITORY Restic repository URL (e.g., s3:s3.amazonaws.com/bucket, /data/backups)

For Local Backups

Variable Description
TARGET_DIR Directory where backup files will be stored

Optional

Variable Description
NOTIFY_URL Optional webhook URL to call when backup completes

Database-specific

For MariaDB containers:

Variable Description
MARIADB_ROOT_PASSWORD Root password for MariaDB

For PostgreSQL containers:

Variable Description
DB_USER PostgreSQL username
PGPASSWORD PostgreSQL password
DB_NAME PostgreSQL database name

Usage

Backup Methods

The container supports three execution modes:

  1. restic - Backup to Restic repositories (cloud storage, remote servers)
  2. local - Create compressed local backup files
  3. loop - Keep container running for external schedulers (e.g., Ofelia, Kubernetes CronJob)

Restic Backups

docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e CONTAINER=my-mariadb-container \
  -e RESTIC_PASSWORD=my-secret-password \
  -e RESTIC_REPOSITORY=s3:s3.amazonaws.com/my-backup-bucket \
  -e MARIADB_ROOT_PASSWORD=db-password \
  gitea.ceperka.net/rosti/db-backup:latest restic

Local Backups

docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /host/backup/path:/backups \
  -e CONTAINER=my-mariadb-container \
  -e TARGET_DIR=/backups \
  -e MARIADB_ROOT_PASSWORD=db-password \
  gitea.ceperka.net/rosti/db-backup:latest local

Loop Mode (For External Schedulers)

docker run -d \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e CONTAINER=my-mariadb-container \
  -e MARIADB_ROOT_PASSWORD=db-password \
  gitea.ceperka.net/rosti/db-backup:latest loop

In loop mode, the container stays running indefinitely, allowing external schedulers like Ofelia, Kubernetes CronJobs, or other orchestrators to execute the backup scripts directly inside the running container.

With Docker Compose

Restic Backup Setup

version: '3.8'

services:
  database:
    image: mariadb:latest
    environment:
      MARIADB_ROOT_PASSWORD: secretpassword
      MARIADB_DATABASE: myapp
    volumes:
      - db_data:/var/lib/mysql

  backup-restic:
    image: gitea.ceperka.net/rosti/db-backup:latest
    depends_on:
      - database
    environment:
      CONTAINER: database
      RESTIC_PASSWORD: my-backup-password
      RESTIC_REPOSITORY: s3:s3.amazonaws.com/my-backup-bucket
      MARIADB_ROOT_PASSWORD: secretpassword
      NOTIFY_URL: https://hc-ping.com/your-healthcheck-uuid
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: ["restic"]

volumes:
  db_data:

Local Backup Setup

version: '3.8'

services:
  database:
    image: mariadb:latest
    environment:
      MARIADB_ROOT_PASSWORD: secretpassword
      MARIADB_DATABASE: myapp
    volumes:
      - db_data:/var/lib/mysql

  backup-local:
    image: gitea.ceperka.net/rosti/db-backup:latest
    depends_on:
      - database
    environment:
      CONTAINER: database
      TARGET_DIR: /backups
      MARIADB_ROOT_PASSWORD: secretpassword
      NOTIFY_URL: https://hc-ping.com/your-healthcheck-uuid
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./backups:/backups
    command: ["local"]

volumes:
  db_data:

Loop Mode with External Scheduler (Ofelia)

version: '3.8'

services:
  database:
    image: mariadb:latest
    environment:
      MARIADB_ROOT_PASSWORD: secretpassword
      MARIADB_DATABASE: myapp
    volumes:
      - db_data:/var/lib/mysql

  backup:
    image: gitea.ceperka.net/rosti/db-backup:latest
    depends_on:
      - database
    environment:
      CONTAINER: database
      TARGET_DIR: /backups
      MARIADB_ROOT_PASSWORD: secretpassword
      NOTIFY_URL: https://hc-ping.com/your-healthcheck-uuid
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./backups:/backups
    command: ["loop"]
    labels:
      ofelia.enabled: "true"
      ofelia.job-exec.backup-local.schedule: "0 2 * * *"
      ofelia.job-exec.backup-local.command: "/backup_local.sh"
      ofelia.job-exec.backup-restic.schedule: "0 3 * * *" 
      ofelia.job-exec.backup-restic.command: "/backup_restic.sh"

  scheduler:
    image: mcuadros/ofelia:latest
    depends_on:
      - backup
    command: daemon --docker
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

volumes:
  db_data:

Scheduled Backups with Cron

To run backups on a schedule, you can use cron or a container orchestrator:

Restic Backups

# Add to crontab for daily Restic backups at 2 AM
0 2 * * * docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -e CONTAINER=my-db -e RESTIC_PASSWORD=pass -e RESTIC_REPOSITORY=s3:s3.amazonaws.com/bucket -e MARIADB_ROOT_PASSWORD=dbpass gitea.ceperka.net/rosti/db-backup:latest restic

Local Backups

# Add to crontab for daily local backups at 3 AM
0 3 * * * docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v /host/backups:/backups -e CONTAINER=my-db -e TARGET_DIR=/backups -e MARIADB_ROOT_PASSWORD=dbpass gitea.ceperka.net/rosti/db-backup:latest local

External Schedulers with Loop Mode

When using loop mode, you can execute backups from external schedulers by running the backup scripts directly inside the running container:

With Ofelia Scheduler

Ofelia can execute jobs in running containers using labels (see Docker Compose example above).

Manual Execution in Loop Mode

# Execute local backup in running container
docker exec <container-name> /backup_local.sh

# Execute restic backup in running container  
docker exec <container-name> /backup_restic.sh

With Kubernetes CronJob + Running Pod

# Execute backup in running pod
kubectl exec <pod-name> -- /backup_local.sh

Kubernetes CronJob

Restic Backup CronJob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: database-backup-restic
spec:
  schedule: "0 2 * * *"  # Daily at 2 AM
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: gitea.ceperka.net/rosti/db-backup:latest
            args: ["restic"]
            env:
            - name: CONTAINER
              value: "my-database-pod"
            - name: RESTIC_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: backup-secrets
                  key: restic-password
            - name: RESTIC_REPOSITORY
              value: "s3:s3.amazonaws.com/my-backup-bucket"
            - name: MARIADB_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secrets
                  key: root-password
            volumeMounts:
            - name: docker-sock
              mountPath: /var/run/docker.sock
          volumes:
          - name: docker-sock
            hostPath:
              path: /var/run/docker.sock
          restartPolicy: OnFailure

Local Backup CronJob

apiVersion: batch/v1
kind: CronJob
metadata:
  name: database-backup-local
spec:
  schedule: "0 3 * * *"  # Daily at 3 AM
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: gitea.ceperka.net/rosti/db-backup:latest
            args: ["local"]
            env:
            - name: CONTAINER
              value: "my-database-pod"
            - name: TARGET_DIR
              value: "/backups"
            - name: MARIADB_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secrets
                  key: root-password
            volumeMounts:
            - name: docker-sock
              mountPath: /var/run/docker.sock
            - name: backup-storage
              mountPath: /backups
          volumes:
          - name: docker-sock
            hostPath:
              path: /var/run/docker.sock
          - name: backup-storage
            persistentVolumeClaim:
              claimName: backup-pvc
          restartPolicy: OnFailure

Backup File Naming

Restic Backups

Backups are stored with the following naming convention:

  • MariaDB: mariadb_[CONTAINER]_[DB_NAME].sql
  • PostgreSQL: pgsql_[CONTAINER]_[DB_NAME].sql

Local Backups

Local backup files include timestamps and are compressed:

  • MariaDB: YYYYMMDD_HHMMSS_mariadb_[CONTAINER]_[DB_NAME].sql.zst
  • PostgreSQL: YYYYMMDD_HHMMSS_pgsql_[CONTAINER]_[DB_NAME].sql.zst

Local backups use zstd compression for efficient storage and include atomic file operations (temporary files are renamed when complete).

MariaDB Backup Features

The container includes comprehensive MariaDB backup options:

  • --add-drop-trigger - Add DROP TRIGGER statements
  • --add-drop-table - Add DROP TABLE statements
  • --add-drop-database - Add DROP DATABASE statements
  • --hex-blob - Use hexadecimal notation for binary data
  • --compress - Compress data in backup
  • --events - Include events in backup
  • --routines - Include stored procedures and functions
  • --single-transaction - Consistent backup for InnoDB tables
  • --triggers - Include triggers in backup

Notification Support

Both backup methods support optional webhook notifications:

# Set NOTIFY_URL to receive notifications when backups complete
-e NOTIFY_URL=https://hc-ping.com/your-healthcheck-uuid

The container will make a GET request to the URL after successful backup completion.

Supported Restic Repositories

This container supports all Restic repository types:

  • Local: /path/to/backup/dir
  • SFTP: sftp:user@host:/path/to/repo
  • S3: s3:s3.amazonaws.com/bucket
  • Azure: azure:container:/path
  • Google Cloud: gs:bucket:/path
  • Backblaze B2: b2:bucket:/path
  • REST: rest:http://host:8000/repo

Building

# Build the image
task build

# Tag as latest
task tag-latest

# Push to registry
task push

Or manually:

docker build -t gitea.ceperka.net/rosti/db-backup:dev .

Troubleshooting

Common Issues

  1. "Docker is not available"

    • Ensure Docker socket is mounted: -v /var/run/docker.sock:/var/run/docker.sock
    • Check Docker daemon is running
  2. "Unsupported database type"

    • Verify the target container has mariadb-dump or pg_dump installed
    • Check container name is correct
  3. Authentication errors

    • Verify database credentials are correct
    • Ensure environment variables are properly set
  4. "TARGET_DIR does not exist" (Local backups)

    • Ensure the target directory is mounted as a volume
    • Check directory permissions
  5. "Unknown backup method"

    • Ensure you specify either local or restic as the command argument

Debug Mode

To debug issues, you can run the container interactively:

Restic Debug

docker run -it --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  --entrypoint /bin/bash \
  gitea.ceperka.net/rosti/db-backup:latest

Local Debug

docker run -it --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /host/backup/path:/backups \
  --entrypoint /bin/bash \
  gitea.ceperka.net/rosti/db-backup:latest

Security Considerations

  • Store sensitive environment variables in secrets (Kubernetes secrets, Docker secrets, etc.)
  • Use least-privilege access for Docker socket when possible
  • Regularly rotate Restic repository passwords
  • Consider using encrypted storage for backup repositories