A lightweight deployment agent that watches a Git repository and automatically deploys Docker Compose stacks when changes are detected. It’s designed for simple, Git-driven deployments on a single host. I wanted something like this because my servers are not accessible by the public, but don't have the hardware to easily run my own git infrastructure/github runners and wanted a bit more flexibility than a simple bash script.
- Clones (or pulls) a Git repository on a fixed interval
- Detects changes on a configured branch
- Determines which Docker Compose stacks were modified
- Pulls updated images and runs docker compose up -d
- Optionally injects secrets from
/secrets/<stack>.env
It can deploy:
- Only changed stacks (default)
- All stacks on every change (forced mode)
Expected structure inside the Git repo:
/stacks
├── stack-a/
│ ├── docker-compose.yml
│ └── ...
├── stack-b/
│ ├── compose.yaml
│ └── ...
Each top-level directory containing a Compose file is treated as a stack. Supported filenames:
docker-compose.ymldocker-compose.yamlcompose.ymlcompose.yaml
If a secrets file exists at:
/secrets/<stack-name>.env
It will be copied to:
/stacks/<stack-name>/.env
before deployment.
If no secrets file exists, the stack is deployed without a .env.
| Variables | Default | Descrition |
|---|---|---|
GIT_REPO |
not set (required) | Git repository URL |
BRANCH |
main |
Branch to track |
CHECK_INTERVAL |
300 |
Polling interval in seconds |
COMPOSE_DIR |
not set (root) | Subdirectory to use under /stacks, so by default it assumes that all compose files are under here |
FORCE_DEPLOY_ALL |
False |
Redeploy all stacks, even when just one changes |
LOG_LEVEL |
INFO |
Logging level |
NOTIFY_GOTIFY |
false |
Enables/disables notifications via gotify |
GOTIFY_URL |
not set | |
GOTIFY_TOKEN |
not set | |
GOTIFY_LEVEL |
not set | Log level that needs to be pushed |
NOTIFY_DISCORD |
false |
Enables/disables notifications via discord |
DISCORD_WEBHOOK |
not set | |
DISCORD_LEVEL |
not set | Log level that needs to be pushed |
NOTIFY_NTFY |
false |
Enables/disables notifications via ntfy |
NTFY_TOPIC |
not set | |
NTFY_LEVEL |
not set | Log level that needs to be pushed |
Example compose.yml:
services:
deploy-agent:
image: ghcr.io/woutnerd/pull-cd:latest
restart: unless-stopped
environment:
GIT_REPO: https://github.com/your-org/your-stacks.git
BRANCH: main
CHECK_INTERVAL: 300
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./stacks:/stacks
- ./secrets:/secretsImportant
The Docker socket must be mounted for the agent to control Docker on the host.
- Clones the entire repo (if needed)
- Deploys all detected stacks
- Fetches latest changes
- If
FORCE_DEPLOY_ALL=False:- Deploys only stacks affected by the Git diff
- If
FORCE_DEPLOY_ALL!=False:- Deploys all stacks
Currently notications are the same as logs. For every notification channel the desired log level can be set independently.