A minimal Docker container auto-updater, a Watchtower alternative. Automatically updates running containers when new images are available.
Philosophy: Keep It Simple, Stupid (KISS) - No web UI, no config files, no database.
Why did i build this when so many other projects exists? First of all, Watchtower was archived, so I needed a new way to automatically pull Docker image updates.Most of the other projects I found felt way more complex than what I wanted. I just wanted a small application that updates images to the latest version and sends a notification when it’s done. So I started this project. The philosophy is to keep the application as small as possible, to avoid introducing security issues, and to only use the libraries I actually need. Right now it only depends on the Go Docker library, and I want to keep it that way. I’m not a programming expert, so I also used help from Claude Code, Reddit, and Google to build this. I made this application mostly for fun. If you find a bug, please open an issue. If you want a new feature, you’ll probably want to fork it, as I don’t intend to make it more complex. I use Renovate and govulncheck to keep dependencies up to date and to check for vulnerable or compromised packages. This is automated in my homelab, and I’ll release new versions when updates are needed. If you're a Go expert and notice any improvements or logical errors, please open an issue. I’d really appreciate the help.
- Opt-in only via
io.repull.enable=truelabel - Docker Compose aware (groups services correctly)
- Multi-network container support
- Preserves all container config (volumes, ports, networks, env vars, etc.)
- Interval or scheduled updates
- Discord webhook notifications
- Dry-run mode
- Single static binary, no dependencies
services:
repull:
image: fanuelsen/repull
container_name: repull
restart: unless-stopped
environment:
- DOCKER_HOST=tcp://socket-proxy:2375
- REPULL_INTERVAL=300 # Check every 5 minutes
# - REPULL_SCHEDULE=23:00 # Or run daily at 11 PM
# - REPULL_DISCORD_WEBHOOK=https://discord.com/api/webhooks/...
networks:
- socket-proxy
# Add an external network if using Discord webhooks (needs internet)
labels:
- "io.repull.enable=true"
socket-proxy:
image: tecnativa/docker-socket-proxy
container_name: socket-proxy
restart: unless-stopped
privileged: true
userns_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
CONTAINERS: 1
IMAGES: 1
NETWORKS: 1
POST: 1
networks:
- socket-proxy
networks:
socket-proxy:
internal: true # No internet access needed for pulling imagesNote: Repull doesn't need internet access for pulling images - the Docker daemon on the host does the actual pulling. Repull only needs internet if you're using Discord webhook notifications.
Download from GitHub Releases:
chmod +x repull
sudo mv repull /usr/local/bin/
# Run once
repull
# Run every 5 minutes
repull --interval 300
# Run daily at 11 PM
repull --schedule 23:00Add io.repull.enable=true to containers you want auto-updated:
services:
app:
image: myapp:latest
labels:
io.repull.enable: "true"| Label | Value | Description |
|---|---|---|
io.repull.enable |
true |
Opt this container in to auto-updates |
# Single check
repull
# Continuous (every 5 minutes)
repull --interval 300
# Scheduled (daily at specific time)
repull --schedule 23:00
# With Discord notifications
repull --interval 300 --discord-webhook "https://discord.com/api/webhooks/..."
# Dry-run (preview only)
repull --dry-run
# Remove replaced images after updating (keeps disk usage in check)
repull --interval 300 --cleanup| Flag | Env Variable | Description |
|---|---|---|
--interval N |
REPULL_INTERVAL |
Run every N seconds (0 = single run) |
--schedule HH:MM |
REPULL_SCHEDULE |
Run daily at specific time |
--discord-webhook URL |
REPULL_DISCORD_WEBHOOK |
Discord webhook for notifications |
--dry-run |
REPULL_DRY_RUN |
Preview changes without applying |
--cleanup |
REPULL_CLEANUP |
Remove the replaced image after a successful update |
--docker-host HOST |
DOCKER_HOST |
Docker daemon address |
Note: --interval and --schedule are mutually exclusive.
Note: Prefer REPULL_DISCORD_WEBHOOK over --discord-webhook for the webhook URL. CLI flags are visible to other processes via /proc/<pid>/cmdline, whereas environment variables are not.
- Lists all running containers
- Filters for
io.repull.enable=truelabel - Groups by Docker Compose service
- Pulls the latest image
- Compares each container's image ID against the freshly pulled image
- Recreates containers running an outdated image (preserving all config)
Repull can update itself. If you add io.repull.enable=true to repull's own container, it will pull new images and recreate itself just like any other container. If you don't want repull to self-update, simply don't add the label — repull only touches containers that are explicitly opted in.
Note: Run only one repull instance per Docker daemon. At startup, repull removes older repull containers (label io.repull.app=true) left over from previous self-updates — a deliberately-running second instance would be removed too.
The Docker daemon does not store registry credentials — docker login saves them client-side in ~/.docker/config.json, and every client (the Docker CLI, repull, ...) must send them along with each pull. Repull reads the same config.json, so private registries work as long as repull can see that file.
When running repull in a container, mount the config read-only:
services:
repull:
image: fanuelsen/repull
volumes:
- ~/.docker/config.json:/home/repull/.docker/config.json:ro
# ...When running the binary directly, it just works for the user that ran docker login.
Limitation: only inline auths entries are supported (the default on Linux servers). Credential helpers (credsStore / credHelpers, e.g. Docker Desktop's keychain integration) are not — if your config.json uses one, create a config file with inline credentials for repull instead:
echo '{"auths":{"ghcr.io":{"auth":"'$(echo -n 'USERNAME:TOKEN' | base64)'"}}}' > /path/to/repull-docker/config.jsonand mount it as shown above (or point the DOCKER_CONFIG environment variable at its directory).
- Docker Hub:
fanuelsen/repull:latest - GitHub:
ghcr.io/fanuelsen/repull:latest
- Docker Engine (local or remote)
- Docker socket access (
/var/run/docker.sock)
Want to add a web UI? Kubernetes support? GraphQL API? Please fork it instead. This project is intentionally minimal.
