The auto git puller and docker builder for Synology NAS - automatically monitors a git repository for changes and builds Docker images when changes are detected.
- 🔄 Automatically monitors git repositories for changes
- 🐳 Builds Docker images using Docker-in-Docker when changes are detected
- ⏰ Configurable check interval (default: every 1 hour)
- 🔐 Supports both public and private repositories with authentication
- 📦 Minimal Alpine Linux base image for efficiency
- 🏷️ Automatic image tagging with timestamps and branch names
- 🌿 Multiple branch support - monitor and build multiple branches simultaneously
- 🔒 Secure credential management via mounted files or environment variables
docker pull ghcr.io/wuhao5/syno-builder:latestOr build locally:
docker build -t syno-builder .Create a file with your Personal Access Token:
echo "ghp_your_token_here" > /path/to/pat
chmod 600 /path/to/patWith mounted PAT file (recommended):
docker run -d \
--name syno-builder \
-e GIT_REPO=https://github.com/user/repo.git \
-e GIT_BRANCH=main \
-v /path/to/pat:/app/secrets/pat:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
-v syno-builder-state:/app/state \
--restart unless-stopped \
ghcr.io/wuhao5/syno-builder:latestWith environment variable:
docker run -d \
--name syno-builder \
--env-file .env \
-v /var/run/docker.sock:/var/run/docker.sock \
-v syno-builder-state:/app/state \
--restart unless-stopped \
ghcr.io/wuhao5/syno-builder:latestImportant: The -v /var/run/docker.sock:/var/run/docker.sock mount is required for Docker-in-Docker functionality.
All configuration is done through environment variables:
GIT_REPO- The git repository URL to monitor (e.g.,https://github.com/username/repo.git)
GIT_BRANCH- The branch(es) to monitor (default:main)- Single branch:
GIT_BRANCH=main - Multiple branches:
GIT_BRANCH=main,develop,staging(comma-separated)
- Single branch:
GIT_PAT_FILE- Path to mounted file containing Personal Access Token (default:/app/secrets/pat)GIT_PAT- Personal Access Token as environment variable (fallback if file doesn't exist)CHECK_INTERVAL- How often to check for changes in minutes (default:60)- For best results, use intervals that divide evenly into 60 (e.g., 1, 5, 10, 15, 30, 60)
- For intervals > 60 minutes, use multiples of 60 (e.g., 120, 180, 240 for 2, 3, 4 hours)
DOCKERFILE_PATH- Path to the Dockerfile within the repository (default:.)DOCKER_IMAGE_NAME- Name for the built Docker image (default:auto-built-image)IMAGE_TAG- Docker image tag (optional)- If not provided, defaults to:
{DOCKER_IMAGE_NAME}:{branch}-{git-hash-7chars}(only whenSHOULD_TAG_EACH_BUILD=true) - Example default:
auto-built-image:main-abc1234 - If provided, uses the custom tag:
IMAGE_TAG=myapp:custom-tag
- If not provided, defaults to:
SHOULD_TAG_EACH_BUILD- Enable unique tag for each build (default:false)- When
false: Only tags withIMAGE_BRANCH(e.g.,myapp:main) - When
true: Also tags withIMAGE_TAGcontaining git hash (e.g.,myapp:main-abc1234) - Set to
trueif you want to preserve each build with a unique, commit-based tag
- When
BUILD_SCRIPT- Path to a custom build script that replaces the default docker build command (optional)- Use this to customize the build process, use alternative build tools (like buildx, Podman), or add custom logic
- The script receives all necessary environment variables:
BUILD_CONTEXT,DOCKERFILE_FULL_PATH,IMAGE_TAG,IMAGE_BRANCH,IMAGE_LATEST,BRANCH,BRANCH_SAFE,DOCKER_IMAGE_NAME,GIT_COMMIT_HASH,GIT_COMMIT_SHORT,GIT_BRANCH_NAME,GIT_REPO_URL,BUILD_TIMESTAMP,REPO_DIR - The script should exit with status 0 on success, non-zero on failure
- Example:
/app/scripts/custom-build.sh - See
scripts/build.sh.examplefor examples
POST_BUILD_SCRIPT- Path to a script to run after successful build (optional)- The script runs in detached mode, so long-running processes like
docker compose up -dwon't block - Available environment variables in the script:
IMAGE_TAG,IMAGE_BRANCH,IMAGE_LATEST,BRANCH,DOCKER_IMAGE_NAME,REPO_DIR - Example:
/app/scripts/post-build.sh - See
scripts/post-build.sh.examplefor examples
- The script runs in detached mode, so long-running processes like
The following environment variables are automatically passed to the docker build process as build arguments:
GIT_COMMIT_HASH- Full commit hash of the current buildGIT_COMMIT_SHORT- Short commit hash (first 7 characters)GIT_BRANCH- Branch name being builtGIT_REPO- Git repository URLBUILD_TIMESTAMP- Timestamp of the build (YYYYMMDD-HHMMSS format)
To use these in your Dockerfile:
ARG GIT_COMMIT_HASH
ARG GIT_BRANCH
ARG BUILD_TIMESTAMP
ENV GIT_COMMIT=${GIT_COMMIT_HASH}
ENV GIT_BRANCH=${GIT_BRANCH}
LABEL build.timestamp=${BUILD_TIMESTAMP}Docker build secrets are automatically available if you mount secret files to /app/secrets directory.
- Each file in
/app/secrets(exceptpat) will be passed to docker build using BuildKit's--secretflag - The
patfile is reserved for git authentication and will not be included as a build secret - If the directory doesn't exist or is empty, no secrets will be available during the build
- Mount your secrets with:
-v /path/to/local/secrets:/app/secrets:ro
Example usage in Dockerfile with BuildKit secrets:
# Access secrets during build - secrets are NOT persisted in image layers when used this way
# Each file in /app/secrets (except 'pat') is passed as --secret id=<filename>,src=<filepath>
RUN --mount=type=secret,id=api-key,target=/run/secrets/api-key \
export API_KEY=$(cat /run/secrets/api-key) && \
# Use API_KEY for configuration without persisting it
echo "API key loaded for build"
# WARNING: Copying secrets to the image will persist them in layers (not recommended)
# RUN --mount=type=secret,id=api-key,target=/run/secrets/api-key \
# cp /run/secrets/api-key /app/config/api-key.txtExample: If you have /path/to/secrets/database.conf and /path/to/secrets/api.key, and optionally /path/to/secrets/pat for git auth, mount them with:
-v /path/to/secrets:/app/secrets:roThe PAT file will be used for git authentication, and the other files will be passed as build secrets:
--secret id=database.conf,src=/app/secrets/database.conf--secret id=api.key,src=/app/secrets/api.key- (PAT file is excluded from build secrets)
Docker Compose example:
volumes:
- /path/to/secrets:/app/secrets:ro # Contains pat, database.conf, api.key, etc.Single branch:
GIT_REPO=https://github.com/myuser/myapp.git
GIT_BRANCH=develop
CHECK_INTERVAL=30
DOCKERFILE_PATH=docker
DOCKER_IMAGE_NAME=myappMultiple branches:
GIT_REPO=https://github.com/myuser/myapp.git
GIT_BRANCH=main,develop,staging
CHECK_INTERVAL=60
DOCKER_IMAGE_NAME=myapp-
Create a file containing your Personal Access Token:
echo "your_token_here" > /path/to/pat chmod 600 /path/to/pat
-
Mount the file when running the container:
-v /path/to/pat:/app/secrets/pat:ro
Set the token in the GIT_PAT environment variable (less secure, not recommended for production).
- Go to GitHub Settings → Developer settings → Personal access tokens
- Generate a new token with
reposcope - Set the token in the
GIT_PATenvironment variable
This solution supports any git provider (GitHub, GitLab, Bitbucket, self-hosted, etc.). The authentication mechanism automatically detects the hostname from your GIT_REPO URL and configures credentials accordingly.
For GitLab, Bitbucket, or other providers:
- Use their respective Personal Access Tokens or App Passwords
- Set the token in the
GIT_PATenvironment variable - The system will automatically configure credentials for the correct hostname
The token will be automatically used for authentication when cloning and pulling from the repository.
Security Note:
- Never commit your
.envfile with real credentials to version control - The PAT is stored securely in the container using git credential helper
- Ensure your
.envfile has restricted permissions:chmod 600 .env - On Synology NAS, consider using Docker Compose secrets or environment variables in the Docker UI instead of a
.envfile for production use
- Initial Run: On startup, the container immediately checks the repository and builds if a Dockerfile is found
- Change Detection: The container tracks the latest commit hash per branch in
/app/state/last_commit_<branch>.txt - Periodic Checks: A cron job runs at the specified interval to check for new commits on all configured branches
- Automatic Build: When changes are detected on any branch, Docker builds the image from that branch
- Tagging:
- By default, each branch build is tagged with branch name and git commit hash (e.g.,
myapp:main-abc1234) - Each branch gets a persistent tag (e.g.,
myapp:main,myapp:develop) - Main/master branch also tagged as
latest - Custom tags can be provided via the
IMAGE_TAGenvironment variable
- By default, each branch build is tagged with branch name and git commit hash (e.g.,
/var/run/docker.sock- Required for Docker-in-Docker/app/state- Recommended for persisting commit tracking across container restarts/app/secrets/pat- Optional mounted file containing Personal Access Token
View the logs to monitor the build process:
# View live logs
docker logs -f syno-builder
# View cron job logs
docker exec syno-builder tail -f /var/log/cron.log# 1. Create a directory for the project
mkdir ~/syno-builder
cd ~/syno-builder
# 2. Create .env file
cat > .env << EOF
GIT_REPO=https://github.com/username/myproject.git
GIT_BRANCH=main
GIT_PAT=ghp_yourtoken
CHECK_INTERVAL=60
DOCKER_IMAGE_NAME=myproject
EOF
# 3. Build syno-builder
docker build -t syno-builder .
# 4. Run syno-builder
docker run -d \
--name syno-builder \
--env-file .env \
-v /var/run/docker.sock:/var/run/docker.sock \
-v syno-builder-state:/app/state \
--restart unless-stopped \
syno-builder- Check that
GIT_REPOis set correctly - Verify credentials if using a private repository
- Ensure the repository contains a valid Dockerfile at the specified path
- Check Docker socket is mounted:
/var/run/docker.sock:/var/run/docker.sock - Verify you have permissions to access the Docker socket on Synology
- Check the cron logs:
docker exec syno-builder tail -f /var/log/cron.log - Verify the
CHECK_INTERVALis set correctly - Ensure the container has network access to fetch from git
docker exec syno-builder /app/check-and-build.shNot supported - you must restart the container with a new CHECK_INTERVAL value.
Run multiple syno-builder containers, one for each repository:
docker run -d --name syno-builder-repo1 --env-file .env.repo1 -v /var/run/docker.sock:/var/run/docker.sock syno-builder
docker run -d --name syno-builder-repo2 --env-file .env.repo2 -v /var/run/docker.sock:/var/run/docker.sock syno-builderYou can replace the default docker build command with a custom build script by setting the BUILD_SCRIPT environment variable. This is useful for:
- Using alternative build tools (e.g., docker buildx for multi-platform builds, Podman)
- Adding custom pre-build or post-build processing
- Implementing custom build logic or optimization
- Using additional build arguments or flags
# Create your custom build script
cat > /path/to/custom-build.sh << 'EOF'
#!/bin/bash
set -e
echo "Custom build: Building Docker image"
echo "Image tag: $IMAGE_TAG"
# Example: Using docker buildx for multi-platform builds
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg GIT_COMMIT_HASH="$GIT_COMMIT_HASH" \
--build-arg GIT_BRANCH="$GIT_BRANCH_NAME" \
-t "$IMAGE_TAG" \
-t "$IMAGE_BRANCH" \
"$BUILD_CONTEXT"
# Tag as latest for main/master branch
if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
docker tag "$IMAGE_TAG" "$IMAGE_LATEST"
fi
echo "Custom build completed"
EOF
chmod +x /path/to/custom-build.sh
# Mount and configure the script
docker run -d \
--name syno-builder \
-e GIT_REPO=https://github.com/user/repo.git \
-e BUILD_SCRIPT=/app/custom/custom-build.sh \
-v /path/to/custom-build.sh:/app/custom/custom-build.sh:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
-v syno-builder-state:/app/state \
--restart unless-stopped \
ghcr.io/wuhao5/syno-builder:latestSee scripts/build.sh.example for more examples of custom build scripts.
Important Notes:
- Custom build scripts are responsible for their own image tagging, including the
latesttag for main/master branches - The script should exit with status code 0 on success, non-zero on failure
- Make sure your custom build script is executable (
chmod +x) before mounting it
Available environment variables in custom build script:
BUILD_CONTEXT- Path to the build context directoryDOCKERFILE_FULL_PATH- Full path to the DockerfileIMAGE_TAG- Image tag with git commit hash (e.g.,myapp:main-abc1234) or custom tag if provided via IMAGE_TAG environment variableIMAGE_BRANCH- Branch image tag (e.g.,myapp:main)IMAGE_LATEST- Latest tag (only for main/master branch)BRANCH- Git branch nameBRANCH_SAFE- Git branch with slashes replaced by dashesDOCKER_IMAGE_NAME- Base image nameGIT_COMMIT_HASH- Full git commit hashGIT_COMMIT_SHORT- Short git commit hash (7 chars)GIT_BRANCH_NAME- Git branch nameGIT_REPO_URL- Git repository URLBUILD_TIMESTAMP- Build timestamp (YYYYMMDD-HHMMSS)REPO_DIR- Path to the cloned repository
You can run custom scripts after a successful build by setting the POST_BUILD_SCRIPT environment variable:
# Create your post-build script
cat > /path/to/post-build.sh << 'EOF'
#!/bin/bash
echo "Deploying $IMAGE_TAG"
docker stop my-app || true
docker rm my-app || true
docker run -d --name my-app "$IMAGE_TAG"
EOF
chmod +x /path/to/post-build.sh
# Mount and configure the script
docker run -d \
--name syno-builder \
-e GIT_REPO=https://github.com/user/repo.git \
-e POST_BUILD_SCRIPT=/app/custom/post-build.sh \
-v /path/to/post-build.sh:/app/custom/post-build.sh:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
-v syno-builder-state:/app/state \
--restart unless-stopped \
ghcr.io/wuhao5/syno-builder:latestThe post-build script runs in detached mode, so long-running commands like docker compose up -d won't block the build process. See scripts/post-build.sh.example for more examples.
When Docker builds images, it creates intermediate layers (cached as <none>:<none> images). These layers are useful for speeding up future builds through layer caching. However, on storage-limited systems like Synology NAS, you may want to periodically clean up unused images.
Recommendation: Keep build cache for performance, but run cleanup periodically if storage is a concern.
# Remove dangling images only (safe, keeps layer cache)
docker exec syno-builder docker image prune -f
# Remove all unused images (more aggressive, may slow down future builds)
docker exec syno-builder docker image prune -a -f
# Full system cleanup (removes unused containers, networks, images, build cache)
docker exec syno-builder docker system prune -fYou can also add cleanup to your post-build script to automatically clean up after each build:
# Add to your post-build script
docker image prune -fNote: The trade-off is between storage space and build speed. Removing cached layers means Docker will need to rebuild those layers from scratch on the next build.
MIT