Skip to content

Deploy via GHCR

The template ships an autonomous release pipeline. Wire it up once and every merge to main produces a fresh container image Watchtower can pick up.

What the pipeline does

TriggerWorkflowResult
Push to maindocker-publish.ymlghcr.io/<owner>/<image>:latest + sha-<short>
Non-release PR mergedprepare-release.ymlOpens prepare-release/vX.Y.Z bump PR, auto-merges, tags vX.Y.Z
vX.Y.Z tag pushedrelease.ymlPushes semver Docker tags, creates GitHub release, posts Discord embed

Rolling latest is handled only by docker-publish.yml. Stable semver tags are handled only by release.yml. The two workflows never collide.

1. Un-freeze the repo guards

Three files gate on github.repository == 'protoLabsAI/protoAgent'. Update them to your fork's path:

  • .github/workflows/prepare-release.yml
  • .github/workflows/release.yml

The docker-publish.yml workflow doesn't have this guard — it runs on any push to main in any clone.

2. Point the image name at your repo

In all three workflow files, update IMAGE_NAME:

yaml
env:
  REGISTRY: ghcr.io
  IMAGE_NAME: protolabsai/my-agent   # ← lowercase; GHCR is case-sensitive

3. Grant GH_PAT access

prepare-release.yml needs a PAT (not the default GITHUB_TOKEN) to push tags that trigger downstream workflows — GITHUB_TOKEN-pushed tags do not fire on: push: tags handlers, by GitHub's design.

Create a fine-grained PAT with contents: write on the repo, then add it as a secret named GH_PAT in Settings → Secrets → Actions.

4. (Optional) Discord release embeds

release.yml calls scripts/post-release-notes.mjs, which reads two env secrets:

  • ANTHROPIC_API_KEY — for Claude Haiku to rewrite raw commits as polished notes. Without it, the script posts raw commit subjects.
  • DISCORD_RELEASE_WEBHOOK — Discord channel webhook URL. Without it, notes print to stdout and never leave CI.

Also configurable via env (override in workflow if you want different branding per deploy):

  • AGENT_NAME — the name that shows in the embed title (defaults to protoAgent)
  • AGENT_TAGLINE — one-line tagline in the embed footer

5. Verify the first push

Merge any PR to main (or push a trivial commit). docker-publish.yml should produce:

ghcr.io/<owner>/<image>:latest
ghcr.io/<owner>/<image>:sha-<short>

Check Actions on the repo and Packages on the org for the image.

6. Point Watchtower at latest

On your deploy host (or wherever your compose / IaC lives):

yaml
services:
  my-agent:
    image: ghcr.io/protolabsai/my-agent:latest
    restart: unless-stopped
    labels:
      - "com.centurylinklabs.watchtower.enable=true"
    environment:
      AGENT_NAME: my-agent
      OPENAI_API_KEY: ${LITELLM_MASTER_KEY}
      LANGFUSE_PUBLIC_KEY: ${LANGFUSE_PUBLIC_KEY}
      LANGFUSE_SECRET_KEY: ${LANGFUSE_SECRET_KEY}
    ports:
      - "7870:7870"
    volumes:
      - audit:/sandbox/audit
      - knowledge:/sandbox/knowledge

  watchtower:
    image: containrrr/watchtower
    command: --interval 60 --label-enable
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

Watchtower polls latest every 60 seconds and recreates the container when the image hash changes.

7. Cut a release

From the Actions tab, run prepare-release.yml manually and pick patch / minor / major. It opens a bump PR, auto-merges it, and tags vX.Y.Z, which triggers release.yml → stable semver Docker tags → GitHub release → Discord post (if configured).

After the first manual run, every non-release PR merge auto-triggers prepare-release.yml with a patch bump. Manual dispatch is only for minor / major bumps.

Part of the protoLabs autonomous development studio.