Self-hosted service
djmaze/resticker avatar
djmaze/resticker

Resticker turns a restic container into a scheduled backup service

Run automatic restic backups via a Docker container.

717 stars84 forksShellApache-2.0

At a glance

What is it?
Resticker is a small set of shell entry points packaged as a Docker image that runs restic on a schedule, with separate backup, prune and check services, and it treats the repository lock and the forget policy as first-class concerns.
Who is it for?
Resticker suits an operator who already trusts restic and wants the scheduling, the retention policy and the check job packaged rather than hand-written, and it suits anyone running the same backup on several nodes through Docker Swarm. It is the wrong tool if you want a backup application rather than a wrapper, since every capability beyond scheduling comes from restic's own command line, and it is the wrong choice if you need a UI, a catalog or a reporting layer.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 108 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The feature list is short and each item is a job you would otherwise write

Resticker runs automatic restic backups via a Docker container, and the features list covers a short list of jobs. It runs scheduled backups. It backs up to any local or remote target supported by restic. It supports tags, exclude definitions and all other optional restic options. It forgets old backups automatically. It prunes backups on a schedule. It removes a stale repository lock. And it can be used as a global Docker swarm service in order to back up every cluster node. The last two are the ones that show this is more than a cron wrapper around the restic binary. The stale lock removal matters because a restic repository that was not closed cleanly refuses every subsequent operation until someone runs unlock, and a backup job that fails forever because of a lock left by a machine that was killed is a real failure mode. The image is multi-architecture, with mazzolino/restic running on amd64 and armv7, and the release history shows the arm64 work followed in the Dockerfile.

Three services, three schedules, one repository

The usage section says to use the supplied example configs to set up a backup schedule, and the Compose files contain a backup, a prune and a check service which can be scheduled independently of each other. The readme also says to feel free to remove the prune and/or check service if you want to run the prune jobs manually, which is a sensible option because pruning is the expensive part and some people prefer to run it on demand. There is a warning about multiple services targeting the same repository: if you have multiple services configured for the same repository, make sure that at most one service is allowed to initialise the repository, or a newly created repository might become corrupt. The fix is a SKIP_INIT=true environment variable on the other services. That is a real operational detail that a naive implementation would get wrong, and the fact that the documentation calls it out suggests the author has seen it happen. The Compose route is to adjust the supplied docker-compose.example.yml as needed and then run docker-compose up -d, and the Swarm route is to adjust docker-swarm.example.yml and deploy it as a stack with docker stack deploy -f docker-swarm.yml backup.

The configuration is environment variables and cron expressions

The configuration options are a list of environment variables, and the cron format is worth noting because it is not the usual one. BACKUP_CRON, PRUNE_CRON and CHECK_CRON are mutually exclusive, and the examples are 0 30 3 * * * to run every night at 3:30 am, 0 0 4 * * * for the prune at 4:00 am and 0 15 5 * * * for the check at 5:15 am. Those have six fields with seconds first, which is possible because a customised go-cron is used that allows the definition of seconds as the first parameter. RUN_ON_STARTUP, set to "true", executes a backup or prune job right on startup in addition to the cron expression, and is disabled by default. RESTIC_REPOSITORY defaults to /mnt/restic and RESTIC_BACKUP_SOURCES defaults to /data, and the readme reminds you to mount the source into the container as a volume. The pass-through arguments are the interesting part, because RESTIC_BACKUP_ARGS, RESTIC_FORGET_ARGS, RESTIC_PRUNE_ARGS and RESTIC_CHECK_ARGS are inserted verbatim into the restic command line, so the tool can do anything restic can do, and the one rule is that you must not use the = form of assignment but spaces between parameter and value, escaping spaces with a backslash.

Retention and checking are configured, not hardcoded

The retention story runs through two variables. RESTIC_FORGET_ARGS, if specified, is run with the given arguments after each backup or before every prune, with the example --prune --keep-last 14 --keep-daily 1, and the documentation points at the restic forget documentation for the options available. RESTIC_PRUNE_ARGS is run with the given arguments, with the example given for B2 concurrent connection settings and verbose logging, such as -o b2.connections=10 --verbose. The check job has the same shape, with RESTIC_CHECK_ARGS and the example --read-data-subset=10% to check a randomly chosen subset of the repository pack files, and the note that without the option only the structure of the repository is checked while the option also checks data at the cost of transferring it from the repository. That distinction is the one that decides whether a check is a cheap structural check or a real data verification, and it is exactly the knob a careful operator wants to be able to turn. There is also a deprecated variable, RESTIC_BACKUP_TAGS, which is superseded by putting the tags into RESTIC_BACKUP_ARGS.

Restoring is a two-command operation

The restore section answers the question every backup tool eventually has to answer, which is how you get a file back on a host where the container is already running via Docker Compose. You use exec.

bash
# Find the latest snapshot for the current host (note the ID)
docker-compose exec app restic snapshots -H <HOSTNAME>
# Restore the given file on the host
docker-compose exec app restic restore --include /path/to/file <ID>

The first command lists the snapshots for the current host and the second restores a single path from the snapshot with the ID you noted. When using Swarm mode you need to manually SSH into the host and run docker exec -it accordingly, which is a real difference between the two deployment models rather than a detail. There is also a general escape hatch, because you can use the same config to run any restic command with the given configuration, and with the Docker Compose setup that is docker-compose run --rm app with the restic arguments, for example docker-compose run --rm app snapshots. The unlock command is a special case, because when given the unlock command the repository check will be skipped, since it will fail on a locked repository either way.

How the image is built and how it is versioned

The Dockerfile is a two-stage build that downloads pinned binaries and verifies them. The builder stage is golang 1.21.5 on alpine 3.17, and it downloads restic 0.19.1, rclone 1.74.3 and the go-cron fork at version 0.0.4, each with a SHA-256 checksum supplied as a build argument, one per architecture for amd64, arm and arm64. A shell case statement maps uname -m to an architecture name and writes the matching checksum file, and a second case statement selects the checksum for that architecture, with an explicit error for an unknown architecture. The downloads go through curl with --fail, the checksum is verified with sha256sum -c, and the binaries are decompressed and moved into place. The version scheme is semantic versioning, and the images under mazzolino/restic are tagged accordingly, with latest built from the latest master commit, one tag per patch release such as 1.6.0, and rolling tags 1.6 and 1 pointing at the latest patch release below that minor or major version. The recommendation is to pin to the latest patch version and update it manually or with an automated process like Renovate. A tag is also generated for each pull request, labelled pr-xxx.

Where this is the wrong choice

Three limits are worth naming. The first is that Resticker is a wrapper, not a backup application. Every capability beyond scheduling and the three jobs comes from the restic command line, so there is no catalog, no reporting, no deduplication across repositories and no web interface, and if you want those you want a different tool. The second is the operational simplicity of the design, which cuts both ways: the container has to have the source mounted, the repository credentials in the environment and the right permissions for the target, and the three services have to be coordinated so that only one of them initialises the repository. The readme's own warning about a corrupt repository is the kind of thing that happens when a configuration is copied to a second node and one variable is missed. The third is the release gap, since the newest tagged release is 1.8.2 from 2025-12-01 while the last push was on 2026-07-07, and the intermediate release history includes a version explicitly marked as BROKEN, which tells you the project ships quickly and occasionally ships a broken image. The Makefile is small, with an image target, a push target and a manifest target that pushes a multi-platform manifest for linux/amd64, linux/arm and linux/arm64.

Editorial conclusion

Resticker suits an operator who already trusts restic and wants the scheduling, the retention policy and the check job packaged rather than hand-written, and it suits anyone running the same backup on several nodes through Docker Swarm. It is the wrong tool if you want a backup application rather than a wrapper, since every capability beyond scheduling comes from restic's own command line, and it is the wrong choice if you need a UI, a catalog or a reporting layer. Before deploying it, decide which of the three services may initialise the repository, because the readme warns that two services initialising the same repository can leave it corrupt, and set SKIP_INIT=true on the others. The last push was on 2026-07-07 and the current release is 1.8.2 from 2025-12-01.

Frequently asked questions

How do I set up scheduled restic backups with Resticker?

Adjust the supplied docker-compose.example.yml as needed and run docker-compose up -d. The Compose files contain a backup, a prune and a check service that can be scheduled independently through the BACKUP_CRON, PRUNE_CRON and CHECK_CRON environment variables, each of which takes a cron expression with seconds as the first field. For Swarm, adjust docker-swarm.example.yml and deploy it with docker stack deploy -f docker-swarm.yml backup.

Why do I need SKIP_INIT=true when running several services against one repository?

The README warns that if you have multiple services configured for the same repository, at most one service is allowed to initialise the repository, or a newly created repository might become corrupt. Setting SKIP_INIT=true on the other services prevents them from trying to create the repository themselves.

How do I restore a file from a Resticker backup?

On a host where the container is running under Docker Compose, use exec: docker-compose exec app restic snapshots -H <HOSTNAME> to find the snapshot ID, then docker-compose exec app restic restore --include /path/to/file <ID> to restore the file. With Swarm you need to SSH into the host and run docker exec -it instead.

Official sources

  1. djmaze/resticker on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/djmaze-resticker.svg)](https://hysenlabs.com/projects/djmaze-resticker)

Was this useful?