Files
nas-webhook/README.md
T

4.4 KiB

nas-webhook

Lightweight webhook container for NAS auto-deploy. Alpine-based with docker-cli and docker-cli-compose included so hooks can run docker compose pull && docker compose up -d.

Image

registry.gitlab.com/bgrolleman/nas-webhook:latest

Built automatically by GitLab CI on every push to main. Image is public — no registry login needed on the NAS to pull it.

How it works

  1. A GitLab CI pipeline builds and pushes a new image (e.g. hugo_basg.nl:latest)
  2. The CI pipeline sends a POST to http://webhook.nas.basg.nl/hooks/<hook-id> with a secret token in the X-Webhook-Token header
  3. The webhook container receives the request, validates the token, and runs deploy.sh <compose-dir>
  4. deploy.sh does docker compose pull && docker compose up -d in the project directory

Deployment on NAS

Place the following files in a directory on the NAS (e.g. /volume1/docker/webhook/):

webhook/
├── docker-compose.yml
├── hooks.json
└── scripts/
    └── deploy.sh

Then bring it up via the UGREEN web interface or docker compose up -d.

The Docker socket (/var/run/docker.sock) must be mounted so the container can run Docker commands. The NAS docker directory (/volume1/docker) must also be mounted so deploy.sh can reach the project compose files.

docker-compose.yml

networks:
  external:
    external: true

services:
  webhook:
    image: registry.gitlab.com/bgrolleman/nas-webhook:latest
    restart: always
    command: ["-hooks", "/config/hooks.json", "-verbose", "-hotreload"]
    volumes:
      - ./hooks.json:/config/hooks.json:ro
      - ./scripts:/scripts:ro
      - /var/run/docker.sock:/var/run/docker.sock
      - /volume1/docker:/volume1/docker
    expose:
      - 9000
    environment:
      - VIRTUAL_HOST=webhook.nas.basg.nl
      - VIRTUAL_PORT=9000
    networks:
      - external

Note: No LETSENCRYPT_HOST — running HTTP only. The ACME challenge (Let's Encrypt) doesn't work for this container because it only exposes port 9000, not 80. Since it's only called by GitLab CI (not a browser), HTTP is fine.

Adding a new project

1. Add a hook to hooks.json

{
  "id": "my-project",
  "execute-command": "/scripts/deploy.sh",
  "command-working-directory": "/scripts",
  "pass-arguments-to-command": [
    {
      "source": "string",
      "name": "/volume1/docker/my-project"
    }
  ],
  "trigger-rule": {
    "match": {
      "type": "value",
      "value": "YOUR_SECRET_TOKEN_HERE",
      "parameter": {
        "source": "header",
        "name": "X-Webhook-Token"
      }
    }
  }
}

Generate a token with: openssl rand -hex 32

The webhook container uses -hotreload so it picks up hooks.json changes without a restart.

2. Add deploy stage to the project's .gitlab-ci.yml

Store the token as a masked CI variable (e.g. DEPLOY_WEBHOOK_TOKEN) in the GitLab project settings.

deploy:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache curl
  script:
    - |
      curl -f -X POST \
        -H "X-Webhook-Token: $DEPLOY_WEBHOOK_TOKEN" \
        http://webhook.nas.basg.nl/hooks/my-project
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  needs: [build]

3. Make sure the project image is public

The NAS pulls images without authentication. Either make the GitLab registry public (project → Settings → General → Visibility → Public) or set up a deploy token and run sudo docker login registry.gitlab.com on the NAS.

Testing a hook manually

curl -X POST \
  -H "X-Webhook-Token: YOUR_TOKEN" \
  http://webhook.nas.basg.nl/hooks/your-hook-id

Current hooks

Hook ID Project Compose dir
hugo-basg bgrolleman/hugo_basg.nl /volume1/docker/basg-hugo

Known issues / pitfalls

  • No HTTPS: Let's Encrypt won't issue a cert because the container doesn't serve HTTP on port 80. Since only GitLab CI calls this endpoint, HTTP is acceptable. If HTTPS is needed in the future, put a separate nginx container in front that handles port 80/443 and proxies to port 9000.
  • Docker socket permissions: The webhook runs as root inside the container (required to use the socket). This is standard for Docker-in-Docker patterns on a home NAS.
  • Image must be public or NAS must be logged in: The deploy script runs docker compose pull which needs registry access. Easiest solution is a public GitLab project.