Skip to main content
Background Image

Automated Docker Updates with Renovate, GitHub, and Portainer GitOps

·605 words·3 mins
Table of Contents

Motivation
#

Anyone managing production container environments knows the problem: images update quickly, security fixes are released constantly, and manually keeping up is tedious and error-prone.

By combining Renovate, GitHub Actions, and Portainer GitOps, this process can be fully automated:

  • Renovate regularly checks the deployed container images and creates pull requests for updates.
  • GitHub Actions handles the automation of Renovate runs.
  • Portainer automatically deploys changes from the Git repository to the Docker environment.

This creates a clean GitOps workflow for Docker updates.


Configuring Renovate
#

Renovate runs via a GitHub Action and monitors a docker-compose repository.
A central config.js file is required, which can look like this (anonymized version):

module.exports = {
  platform: 'github',
  token: process.env.RENOVATE_TOKEN,
  gitAuthor: 'John Doe <john@example.com>',
  username: 'exampleuser',
  repositories: ['exampleuser/docker-compose'],
  onboarding: false,
  hostRules: [
    {
      hostType: 'docker',
      matchHost: 'docker.io',
      username: 'dockeruser',
      password: String(process.env.DOCKER_HUB_PASSWORD || ''),
    },
    {
      hostType: 'docker',
      matchHost: 'ghcr.io',
      username: 'exampleuser',
      password: String(process.env.GHCR_TOKEN || ''),
    }
  ],
};

👉 Important: Credentials (e.g., Docker Hub or GitHub Container Registry) must come exclusively from GitHub Secrets.


GitHub Workflow
#

The corresponding workflow (.github/workflows/renovate.yml) controls Renovate execution:

name: renovate

on:
  workflow_dispatch: # manual trigger
  schedule:          # daily run at 12:00 UTC
    - cron: '0 12 * * *'
  push:              # on push to main
    branches:
      - main

jobs:
  renovate:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Run Renovate
        uses: renovatebot/github-action@v43
        with:
          configurationFile: ${{ github.workspace }}/config.js
          token: ${{ secrets.RENOVATE_TOKEN }}
        env:
          DOCKER_HUB_PASSWORD: ${{ secrets.DOCKER_HUB_PASSWORD }}

Optional Notification
#

Optionally, you can add a notification step, e.g., via ntfy, whenever a Renovate pull request is open. Example:

- name: Check Open PRs and Notify
  env:
    GITHUB_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
    NTFY_URL: ${{ secrets.NTFY_URL }}
    NTFY_TOKEN: ${{ secrets.NTFY_TOKEN }}
  run: |
    echo "Fetching open pull requests..."
    OPEN_PRS=$(gh pr list \
      --repo "flohoss/docker-compose" \
      --state open \
      --assignee "flohoss" \
      --json number,title,url)

    if [ "$(echo "$OPEN_PRS" | jq length)" -gt 0 ]; then
      echo "Open PRs found, sending notifications..."
      echo "$OPEN_PRS" | jq -c '.[]' | while read -r pr; do
        NUMBER=$(echo "$pr" | jq -r '.number')
        TITLE=$(echo "$pr" | jq -r '.title')
        URL=$(echo "$pr" | jq -r '.url')

        curl -s -X POST "$NTFY_URL" \
          -H "Authorization: Bearer $NTFY_TOKEN" \
          -H "X-Tags: twisted_rightwards_arrows" \
          -H "X-Title: PR #${NUMBER} - ${TITLE}" \
          -H "X-Actions: view, Open PR, ${URL}" \
          -d "A Renovate PR needs your attention."
      done
    else
      echo "No open pull requests found."
    fi

Renovate Rules for Docker Images
#

Through the renovate.json file, you can set rules to determine which images are updated or restricted to certain versions:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended"],
  "assignees": ["exampleuser"],
  "packageRules": [
    {
      "matchDatasources": ["docker"],
      "matchPackageNames": ["redis"],
      "allowedVersions": "7.x"
    },
    {
      "matchDatasources": ["docker"],
      "matchPackageNames": ["postgres"],
      "allowedVersions": "17.x"
    },
    {
      "matchDatasources": ["docker"],
      "pinDigests": true
    }
  ]
}

Examples:

  • Redis will only be updated within version 7.
  • Postgres will only be updated within version 17.
  • All images are digest-pinned for reproducible builds.

GitOps with Portainer
#

Portainer can deploy stacks directly from a Git repository.

Example:

  • Stack is based on https://github.com/exampleuser/docker-compose.
  • In Portainer, “Redeploy from Git repository” is enabled.
  • Additionally, a polling interval is configured (e.g., every 5 minutes).

👉 Once Renovate merges a PR and updates the compose.yml, Portainer automatically pulls the changes and redeploys the stack.

Result:

  • No manual intervention for container updates.
  • All changes are versioned and traceable via Git.
  • Portainer ensures automated deployment in the Docker environment.

Conclusion
#

Combining Renovate + GitHub Actions + Portainer GitOps creates a lean and reliable workflow for automated Docker updates:

  • PR-based updates with full transparency
  • Configurable update rules (e.g., specific major versions only)
  • Automatic rollout in the runtime environment

This saves time, reduces risk, and maintains full control. 🚀

Florian Hoss
Author
Florian Hoss