FTP/FTPS pull backup that keeps the last N versions. Rebuilt weekly with current packages.
151
A small Alpine-based container that pulls a directory via FTP/FTPS – for example the web space of a shared hosting account – on a schedule and keeps the last N versions.
Retention is by count, not age: with KEEP=5 you always have the five most recent backups, no matter how long ago they ran. Old versions are only deleted after a new backup has succeeded.
Multi-arch: linux/amd64, linux/arm64, linux/arm/v7 (Raspberry Pi). (Depending on how the image was built – check the tag's OS/ARCH list.)
This image is rebuilt from scratch every week – on the current Alpine base image and without build cache. Every build pulls the latest versions of all included packages (lftp, curl, OpenSSL, CA certificates, BusyBox …), so security fixes land in the image automatically within a week, without you having to do anything.
To benefit from it, simply pull latest regularly (e.g. with Watchtower or docker compose pull && docker compose up -d).
| Tag | Meaning |
|---|---|
latest | Most recent weekly build – recommended |
YYYY-MM-DD | Build of that day – pin this if you need a fixed version or want to roll back |
Only the six most recent dated builds are kept; older tags are removed automatically.
lftp – only changed files are transferred, files deleted on the server are removed locallytar.gz version per run, rotated to KEEP versionsdocker logsservices:
website-backup:
image: usernahme/ftp-backup:latest
container_name: website-backup
restart: unless-stopped
environment:
TZ: Europe/Berlin
CRON_SCHEDULE: "15 3 * * *"
KEEP: "5"
PREFIX: "website"
FTP_HOST: "ftp.example.com"
FTP_USER: "username"
FTP_PASS_FILE: /run/secrets/ftp_pass
REMOTE_DIR: "/"
EXCLUDES: "logs/ cache/"
MAIL_TO: "[email protected]"
SMTP_HOST: "mail.example.com"
secrets:
- ftp_pass
volumes:
- /srv/backup/website:/data
secrets:
ftp_pass:
file: ./ftp_pass.txt
docker compose up -d
docker exec website-backup backup.sh # run a backup now
docker logs -f website-backup
One-shot run without cron:
docker run --rm -e FTP_HOST=... -e FTP_USER=... -e FTP_PASS=... \
-v /srv/backup/website:/data DEIN_DOCKERHUB_NAME/ftp-backup run
/data
├── mirror/ # current incremental mirror
├── versions/
│ ├── website-20261101-031500.tar.gz
│ └── ... # last KEEP versions
└── .last_status # used by the healthcheck
| Variable | Default | Description |
|---|---|---|
FTP_HOST | – | Server, optionally host:port |
FTP_USER | – | Username |
FTP_PASS / FTP_PASS_FILE | – | Password, or path to a file containing it |
REMOTE_DIR | / | Source directory on the server |
KEEP | 5 | Number of versions to keep |
PREFIX | backup | File name prefix |
CRON_SCHEDULE | 15 3 * * * | Schedule (cron syntax) |
TZ | Europe/Berlin | Time zone |
EXCLUDES | – | Space-separated globs, e.g. logs/ cache/ |
USE_FTPS | yes | Enforce TLS |
VERIFY_CERT | yes | Verify the server certificate |
PARALLEL | 4 | Parallel downloads |
RUN_ON_START | no | Run a backup when the container starts |
MAX_AGE_HOURS | 26 | Healthcheck: unhealthy if the last success is older |
| Variable | Default | Description |
|---|---|---|
NOTIFY | error | error, always or never |
MAIL_TO | – | Recipient(s), comma-separated – no mail if empty |
MAIL_FROM | ftp-backup@<hostname> | Sender address |
SMTP_HOST | – | SMTP server – no mail if empty |
SMTP_PORT | 25 | Port (587 for STARTTLS, 465 for SSL) |
SMTP_TLS | none | none, starttls or ssl |
SMTP_USER | – | Username, empty = no authentication |
SMTP_PASS / SMTP_PASS_FILE | – | Password, or path to a file containing it |
SMTP_VERIFY_CERT | yes | Verify the SMTP server certificate |
The mail contains the full log of the run.
mkdir /tmp/restore
tar -xzf /srv/backup/website/versions/website-....tar.gz -C /tmp/restore
# then upload back, e.g.: lftp -u user ftp.example.com -e "mirror -R /tmp/restore /; quit"
Only files are backed up. Databases on the hosting account (e.g. MySQL) are not included.
Content type
Image
Digest
sha256:813906f57…
Size
10.2 MB
Last updated
about 16 hours ago
docker pull usernahme/ftp-backup