Sign inSign up

shellharbor/backfort

By shellharbor

•Updated 5 days ago

Secure Linux backups for files, Docker Compose and databases, with encryption and cloud storage.

Image
Security
Developer tools
Databases & storage
0

221

shellharbor/backfort repository overview

⁠Backfort

Your last line of data defense. Back up deliberately. Restore with confidence.

CI Kubernetes Release License: MIT

Recovery-first backups for Linux files, Docker Compose applications and databases.

Backfort creates full backup bundles, optionally compresses, encrypts and signs them, publishes independent copies to local or cloud storage, and exits.

No resident daemon. No hidden source discovery. No automatic overwrite of production data.

Whether you need a quick recovery point before an upgrade, scheduled offsite backups, or a controlled migration of a Compose application, Backfort keeps the backup plan explicit and the recovery process inspectable.

Source code⁠ · Documentation⁠ · Examples⁠ · Docker deployment guide⁠ · Report an issue⁠

⁠Why Backfort?

A backup is useful only when you can recover from it.

Backfort is built around that principle:

  • Explicit sources: choose the files, Compose definitions, volumes, bind mounts and database services you want to protect.
  • Completed copies only: payload, metadata and checksum are published before the final .complete marker. Interrupted uploads do not appear as recoverable backups.
  • Independent destinations: keep a fast local recovery copy and a separate offsite copy, with a configurable minimum-copy policy.
  • Verification beyond the archive: new backups include per-file SHA-256 hashes. Full verification reads the archive and checks its contents.
  • Controlled recovery: restore into a new or empty directory, inspect the result, then deliberately apply it.
  • Portable execution: use the native Linux CLI, this Docker image, or the Helm deployment for Kubernetes PVC files.

The container runs the same Backfort implementation and uses the same YAML configuration and backup format as a native installation.

⁠What you can back up

⁠Files and directories

Protect application uploads, website files, documents and explicitly selected configuration directories.

Supported capabilities include:

  • GNU tar archives with configurable exclude patterns.
  • Symlink preservation, with explicit opt-in dereferencing.
  • Ownership, ACL, extended-attribute and sparse-file recovery where privileges and the destination filesystem support them.
  • gzip, zstd, or uncompressed archives.
⁠Docker Compose applications

Create a recoverable bundle containing explicitly selected:

  • Compose configuration files.
  • Non-database named volumes.
  • Project-relative bind mounts.
  • Engine-aware database dumps or native export artifacts.

Backfort does not guess what belongs in a backup. It does not automatically copy every volume, .env file or path mounted by an application.

⁠Databases in Compose services
EngineBackup artifact
PostgreSQLCustom-format dump, or plain SQL when configured; optional globals export
MySQL / MariaDBLogical SQL dump with routines, events and triggers
Microsoft SQL ServerNative .bak backup
OracleOracle Data Pump export

Database tools and required privileges must be available inside the selected database container. Dump and import operations also require an in-container timeout command.

A live database-volume archive is not a substitute for an engine-aware dump. Multi-service or application-wide consistency requires your own reviewed quiescing procedure.

⁠Image and version tags

Docker Hub:

docker pull shellharbor/backfort:1.2.0
docker run --rm shellharbor/backfort:1.2.0 --version

The project also distributes the image through GitHub Container Registry:

docker pull ghcr.io/shellharbor/backfort:1.2.0

Release builds target:

  • linux/amd64
  • linux/arm64

The release pipeline publishes exact version tags and moving stable aliases:

  • 1.2.0 — exact release.
  • 1.2 — latest stable release in that minor series.
  • 1 — latest stable release in that major series.
  • latest — latest stable release.

Pin an exact version or image digest for production. Moving aliases are convenient for evaluation, but they are not reproducible deployment references.

The image includes GNU tar, Bash, core utilities, gzip, zstd, age, GnuPG, Minisign, rclone, notification tools, and the Docker CLI with Compose support. Features remain opt-in through configuration.

⁠Quick start: a durable local backup

The following example backs up /srv/application on a Linux host.

Run the host setup and Compose commands from a trusted root shell, or adapt ownership and permissions for your dedicated backup account with Docker access.

⁠1. Prepare the deployment directory
install -d -m 0700 /srv/backfort /srv/backups/backfort
cd /srv/backfort

The application source directory must already exist. Keep the backup destination outside the source tree.

⁠2. Create config.yaml

All paths in this configuration are container paths:

version: 1

settings:
  host_id: server-01
  state_directory: /var/lib/backfort
  temp_directory: /var/tmp/backfort
  lock_file: /var/lib/backfort/backfort.lock
  min_free_mb: 512

destinations:
  - name: local
    type: local
    path: /backups

jobs:
  - name: application-files

    source:
      type: files
      paths:
        - /source
      exclude:
        - "*.log"
        - "*.tmp"
        - "*/cache/*"
      follow_symlinks: false

    destinations:
      - local

    success:
      min_copies: 1

    compression:
      method: gzip
      level: 6

    encryption:
      method: none

    signing:
      method: none

    retention:
      keep_last: 7
      keep_daily: 7
      keep_weekly: 4
      keep_monthly: 6
      min_keep: 1
      max_age_days: 90

version: 1 is the configuration schema version, not the application release number.

Choose a stable, unique host_id for each server writing to shared storage. Automatic discovery and retention are scoped to that identity.

This introductory example is not encrypted. Configure encryption before storing sensitive data offsite.

⁠3. Create docker-compose.yml
services:
  backfort:
    image: shellharbor/backfort:1.2.0
    restart: "no"
    init: true
    read_only: true

    security_opt:
      - no-new-privileges:true

    tmpfs:
      - /tmp:rw,nosuid,nodev,noexec,size=64m

    volumes:
      - ./config.yaml:/etc/backfort/config.yaml:ro
      - backfort-state:/var/lib/backfort
      - backfort-work:/var/tmp/backfort
      - ${BACKFORT_SOURCE_DIR:?Set an absolute source directory}:/source:ro
      - ${BACKFORT_BACKUP_DIR:?Set an absolute backup directory}:/backups

    command: ["-c", "/etc/backfort/config.yaml", "doctor"]

volumes:
  backfort-state:
  backfort-work:
⁠4. Validate, preview and run
export BACKFORT_SOURCE_DIR=/srv/application
export BACKFORT_BACKUP_DIR=/srv/backups/backfort

docker compose pull
docker compose run --rm backfort doctor
docker compose run --rm backfort --dry-run run
docker compose run --rm backfort run

doctor checks configuration and prerequisites. A dry run validates and displays the plan without creating a backup.

The default container command runs doctor and exits. A stopped container is expected: Backfort is a one-shot job, not a long-running service.

⁠Persistent storage and mounts

Container pathPurposeRecommended access
/etc/backfort/config.yamlBackup configurationRead-only
/sourceApplication source filesRead-only
/backupsLocal backup bundlesRead-write, persistent
/var/lib/backfortState and shared lockRead-write, persistent
/var/tmp/backfortArchive working spaceRead-write, sufficient capacity
/tmpShort-lived scratch filesSmall writable tmpfs

Working space must hold the complete working bundle. Do not size it as though Backfort only streams a few small temporary files.

Keep state persistent across invocations so independent runs share the same lock. Do not casually use docker compose down -v, which removes named volumes.

⁠Verify and restore

List completed backups:

docker compose run --rm backfort list --job application-files

Perform full verification:

docker compose run --rm backfort \
  verify latest --job application-files --full

Quick verification checks the final payload checksum and any configured signature. Full verification additionally processes the archive and checks the available per-file hashes.

To rehearse recovery, prepare a separate empty directory:

install -d -m 0700 /srv/backfort-recovery

docker compose run --rm \
  -v /srv/backfort-recovery:/restore \
  backfort restore latest \
  --job application-files \
  --to /restore

Inspect the restored tree before copying anything into production.

Restore requires a new or empty target. There is no general force-overwrite or automatic in-place recovery mode.

For recovery on another server, preserve the original job configuration and host_id, or deliberately select a full backup ID. Changing the identity changes what latest discovers.

Encrypted full verification and restore require the corresponding recovery keys.

⁠A quick backup before an upgrade

Need a recovery point without first defining another persistent job?

docker compose run --rm \
  -e BACKFORT_QUICK_HOST_ID=server-01 \
  backfort quick /source \
  --name before-upgrade \
  --to /backups \
  --state-directory /var/lib/backfort \
  --exclude "*.log" \
  --exclude "*/cache/*"

This uses the normal backup pipeline and saves a non-secret recovery configuration in persistent state:

/var/lib/backfort/quick/before-upgrade.yaml

Reuse it for later operations:

docker compose run --rm backfort \
  -c /var/lib/backfort/quick/before-upgrade.yaml \
  list --job before-upgrade

A stable BACKFORT_QUICK_HOST_ID avoids tying quick backups to changing container hostnames.

Quick commands are convenience workflows. Use a saved YAML job when you need configured encryption, signing, lifecycle hooks or Prometheus metrics.

⁠Send an independent copy to S3 or another cloud

Backfort uses rclone for remote destinations, including:

  • AWS S3.
  • DigitalOcean Spaces.
  • Vultr Object Storage.
  • Cloudflare R2.
  • FTP / FTPS.
  • Dropbox.
  • Yandex Disk.
  • pCloud.
  • Other supported rclone remotes.

Configure the provider and credentials in rclone first. Backfort references the remote name; it does not replace provider-specific setup.

Add a destination to config.yaml:

destinations:
  - name: local
    type: local
    path: /backups

  - name: s3-offsite
    type: rclone
    remote: company-s3
    path: production-backups/backfort/server-01

For an S3-compatible remote, production-backups is the bucket name.

Update the job’s copy policy:

destinations: [local, s3-offsite]

success:
  min_copies: 2

Mount the protected rclone configuration and merge these settings into the existing Compose service, retaining its other mounts:

environment:
  RCLONE_CONFIG: /run/secrets/rclone.conf

volumes:
  - ./rclone.conf:/run/secrets/rclone.conf:ro

Run doctor again after changing credentials or destinations.

Backfort publishes individual bundle objects rather than running a broad rclone sync. The completion marker is published last.

Use separate, appropriately scoped credentials for backup publication and pruning where your operating model permits it.

⁠Encryption and authenticity

⁠Encrypt with age

Replace the job’s encryption block:

encryption:
  method: age
  recipient_env: BACKFORT_AGE_RECIPIENT
  identity_file_env: BACKFORT_AGE_IDENTITY_FILE

Supply the public recipient through your deployment environment. For example, merge this into the Compose service:

environment:
  BACKFORT_AGE_RECIPIENT: "${BACKFORT_AGE_RECIPIENT:?Set an age public recipient}"

The backup writer needs only the public recipient. Keep the private identity on a separate trusted recovery host whenever practical.

For recovery, mount the identity file read-only and set BACKFORT_AGE_IDENTITY_FILE to its container path.

A public-key-only writer can create encrypted backups and perform quick verification, but cannot decrypt, fully verify or restore them.

⁠GPG and Minisign

Backfort also supports:

  • Symmetric GPG encryption using a password referenced by environment-variable name.
  • Asymmetric GPG encryption to exact public-key fingerprints.
  • Multiple recovery recipients.
  • Detached Minisign payload signatures.

Checksums detect corruption. Signatures additionally detect replacement by an attacker who lacks the signing key. Neither protects against an attacker who controls the backup writer and its private signing material.

Never place passwords, tokens or private keys in tracked configuration or image layers.

Encryption and signing documentation⁠

⁠Back up a Docker Compose project

A Compose-source job needs two additional mounts:

volumes:
  - /var/run/docker.sock:/var/run/docker.sock
  - /srv/crm:/srv/crm:ro

Merge them into the existing Backfort service.

The project directory must appear inside Backfort at the same absolute path as on the Docker host. This lets the containerized client and host daemon resolve project paths consistently.

Docker socket access is host-root-equivalent. Mounting the socket read-only does not remove that authority. Grant it only to a trusted Compose backup job, never to an ordinary files-only deployment.

For a quick migration recovery point, the following example selects:

  • compose.yaml.
  • The non-database uploads named volume.
  • The data/uploads bind-mount directory.
  • A plain SQL dump of the crm PostgreSQL database.

Before running it, configure the database credentials through an approved secret source and choose a trusted, already-pulled helper image containing GNU tar and timeout. Pin that helper by digest for reproducibility.

docker compose run --rm \
  -e BACKFORT_QUICK_HOST_ID=server-01 \
  -e BACKFORT_PG_PASSWORD \
  backfort quick-compose /srv/crm \
  --name crm-before-migration \
  --file compose.yaml \
  --to /backups \
  --state-directory /var/lib/backfort \
  --volume uploads \
  --volume-helper-image "${BACKFORT_VOLUME_HELPER_IMAGE:?Set a trusted pre-pulled helper image}" \
  --bind uploads:data/uploads \
  --db crm-postgres:postgres:postgres:backfort:BACKFORT_PG_PASSWORD:crm

Adapt the service, volume, path, database and user names to your actual project.

Repeat --to to publish additional independent copies:

--to rclone:company-s3:production-backups/backfort/crm

Add --min-copies 2 when both destinations are required.

quick-compose emits plain SQL dumps for PostgreSQL, MySQL and MariaDB. The dumps are stored inside the normal backup bundle, not uploaded as unrelated bare SQL objects.

This is a backup of selected project data—not a container checkpoint, image export or automatic capture of a container’s ephemeral writable layer.

⁠Stage Compose recovery

The generated recovery configuration is saved under:

/var/lib/backfort/quick-compose/crm-before-migration.yaml

Stage the backup into a separate empty host directory:

install -d -m 0700 /srv/backfort-crm-recovery

docker compose run --rm \
  -v /srv/backfort-crm-recovery:/restore \
  backfort \
  -c /var/lib/backfort/quick-compose/crm-before-migration.yaml \
  restore-compose latest \
  --job crm-before-migration \
  --to /restore

By default, restore-compose verifies and stages the bundle, then prints recovery actions. It does not start services, create volumes or import databases.

An optional import assistant supports PostgreSQL, MySQL and MariaDB against an explicitly prepared target Compose project. It requires both --apply --confirm and an explicit --project-dir.

Treat that import as a destructive operation against the selected target database. Use an isolated recovery project and review the staged artifacts first.

PostgreSQL globals, SQL Server backups and Oracle exports require reviewed manual recovery procedures.

Compose migration runbook⁠ · Compose and database examples⁠

⁠Retention and protected recovery points

Retention is evaluated separately for each job and destination.

retention:
  keep_last: 7
  keep_daily: 14
  keep_weekly: 8
  keep_monthly: 12
  min_keep: 1
  max_age_days: 90

Backfort combines the configured last, daily, weekly and monthly selections. Optional age expiry removes older unpinned copies, except for the min_keep recovery floor.

Pruning is a separate command. Creating a backup does not automatically prune existing versions.

Preview first:

docker compose run --rm backfort --dry-run prune
docker compose run --rm backfort prune

Pruning applies to configured local and rclone destinations, including S3-compatible storage.

Protect an important completed recovery point:

docker compose run --rm backfort pin BACKUP_ID \
  --reason "Before database migration"

Replace BACKUP_ID with an ID returned by list.

Pinned copies remain outside normal GFS retention and age expiry. Review them periodically: pins can keep storage indefinitely.

Remove a pin when it is no longer needed:

docker compose run --rm backfort unpin BACKUP_ID

Date-range deletion is also available through delete, with explicit UTC boundaries and confirmation. Always inspect its dry-run output before authorizing a purge.

⁠Scheduling

Backfort does not run an internal scheduler. YAML defines backup jobs and policies; cron, systemd timers or Kubernetes CronJobs determine when commands run.

Example host cron entries:

15 2 * * * cd /srv/backfort && BACKFORT_SOURCE_DIR=/srv/application BACKFORT_BACKUP_DIR=/srv/backups/backfort docker compose run --rm backfort run
45 3 * * * cd /srv/backfort && BACKFORT_SOURCE_DIR=/srv/application BACKFORT_BACKUP_DIR=/srv/backups/backfort docker compose run --rm backfort prune

These use the host cron timezone. Adjust the windows to your backup duration and preserve the process exit status.

The shared persistent lock protects against overlapping mutating commands. Keep restart: "no": a container restart should not create an unexpected additional backup.

⁠Monitoring and notifications

⁠Backup freshness

Use the dead man’s switch to detect a missing or stale completed backup:

docker compose run --rm backfort \
  watchdog --job application-files --max-age 24

Run this from an independent monitoring schedule. A backup job that never starts cannot reliably report its own absence.

⁠Prometheus metrics

Saved YAML jobs can publish results through the node_exporter textfile collector:

metrics:
  prometheus:
    textfile_directory: /metrics

Mount an existing writable collector directory at /metrics and configure node_exporter separately.

Metrics cover run outcome, exit code, completion timestamp, duration, payload size and successful or failed destination counts.

Backfort does not expose an HTTP metrics server. Textfile metrics are produced by persistent run jobs, not by quick backup commands.

⁠Notifications and hooks

Optional event notifications support Telegram, ntfy, webhooks and email delivery.

Saved jobs can also run executable pre/post lifecycle hooks with literal arguments and bounded timeouts. Use them for a reviewed application quiescing and cleanup procedure—not arbitrary shell strings.

Post-cleanup is attempted on supported failure and termination paths, but cannot compensate for uncatchable termination or a host power loss. Design hook cleanup to be idempotent.

Monitoring guide⁠ · Hooks and notifications⁠

⁠Kubernetes deployment

The project includes a Helm 3 chart for Kubernetes 1.31+.

Its scope is explicit mounted PVC files, backed up to a separate PVC or configured rclone destination. It uses the same image, configuration and recovery bundles.

From a matching repository checkout:

cp examples/kubernetes/pvc-local.values.yaml values.yaml

# Adapt host_id, PVC names, storage sizes and storage classes.
helm lint charts/backfort --strict -f values.yaml

helm upgrade --install backfort ./charts/backfort \
  --namespace application \
  -f values.yaml \
  --set image.repository=shellharbor/backfort \
  --set image.tag=1.2.0

The deployment provides:

  • Backup and prune CronJobs, initially suspended.
  • Manual doctor, verification and isolated restore Jobs.
  • Read-only source mounts.
  • Persistent state and shared locking.
  • References to existing Secrets.
  • Tokenless workloads without Kubernetes API/RBAC access.

Source PVCs and referenced Secrets must already exist in the release namespace. Review your CSI driver, access modes, permissions and admission policies before enabling schedules.

This is not a cluster backup product. It does not discover Kubernetes resources, orchestrate CSI snapshots or dump database Pods. The Docker Compose adapter requires a Docker host and is not supported by this chart.

Kubernetes deployment guide⁠

⁠Exit codes

Use the actual command result in your scheduler and monitoring:

CodeMeaning
0Complete success
1Some destinations succeeded and some failed
2Invalid arguments, configuration, dependencies or environment
3Operational failure or required copy policy not met

A failed minimum-copy policy does not erase copies that were successfully completed. Inspect the result instead of assuming that every nonzero exit means no recovery copy exists.

⁠Security and operational boundaries

  • The image runs as root by default to support ownership and metadata recovery.
  • Non-root files-only execution is possible when mounted paths and permissions support it, but has narrower recovery capabilities.
  • Keep configuration, credentials, keys, state and backup destinations protected.
  • Do not grant Docker socket access to files-only jobs.
  • Read-only mounts do not make live application data transactionally consistent.
  • Exclude live database volumes and use engine-aware dumps.
  • Explicitly included source files may themselves contain secrets; encrypt those backups appropriately.
  • Keep an independent offsite copy and rehearse recovery.
  • Preserve state and verify a backup with a new image before changing scheduled production jobs.

Backfort currently creates full backups only. Incremental backup chains and deduplication are not implemented.

The release pipeline is configured to smoke-test both supported image architectures and publish OCI metadata, SBOM and provenance attestations. These are supply-chain information, not a guarantee that every storage driver or application recovery scenario is automatically supported.

⁠Documentation, support and license

Backfort is open-source software released under the MIT License⁠.

Keep your backups independent. Keep your recovery rehearsed.

Tag summary

Content type

Image

Digest

sha256:9c3a0e03f…

Size

106.1 MB

Last updated

5 days ago

docker pull shellharbor/backfort