Servery a DevOps

Docker image do GitHub Container Registry přes GitHub Actions

Automatické sestavení Docker image a push do GHCR (ghcr.io) z GitHub Actions – oprávnění, tagy podle větve a verze, cache buildu a stažení image na VPS.

12. 10. 2026 · 4 min čtení

Sestavovat Docker image přímo na produkčním serveru znamená zatěžovat ho buildem a mít na něm zdrojový kód i build nástroje. Čistší je nechat image postavit GitHub Actions, uložit do GitHub Container Registry (GHCR) a na server už jen stáhnout hotový image. Celé to zvládne jeden workflow.

Co je GHCR

  • Registr kontejnerů GitHubu na adrese ghcr.io.
  • Image se jmenují ghcr.io/<vlastnik>/<nazev>:<tag>, vlastník je uživatel nebo organizace (malými písmeny).
  • Image může být veřejný nebo soukromý; oprávnění lze navázat na repozitář.
  • Z GitHub Actions se přihlásíte vestavěným GITHUB_TOKEN, žádné další heslo nepotřebujete.

Aktuální limity a ceny úložiště pro soukromé balíčky ověřte v dokumentaci GitHub Packages.

Workflow

.github/workflows/docker.yml:

name: Docker image

on:
  push:
    branches: [main]
    tags: ["v*.*.*"]
  pull_request:

permissions:
  contents: read
  packages: write

env:
  IMAGE: ghcr.io/${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: docker/setup-buildx-action@v3

      - name: Přihlášení do GHCR
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Tagy a labely
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.IMAGE }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha

      - name: Build a push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Co se v něm děje

permissions: packages: write – bez tohoto oprávnění GITHUB_TOKEN do GHCR zapisovat nemůže a push skončí chybou denied / 403.

Pull requesty jen stavíme. Image se sestaví (ověří se, že Dockerfile funguje), ale nepushne se. Navíc PR z forků nemají oprávnění zapisovat.

docker/metadata-action generuje tagy podle události:

Událost Výsledné tagy
push do main main, sha-1a2b3c4
tag v1.4.2 1.4.2, 1.4, sha-… a ve výchozím nastavení i latest
PR #15 pr-15 (jen build)

Akce také přidá standardní OCI labely (zdrojový repozitář, commit, datum), takže GHCR image automaticky propojí s repozitářem.

Cache type=gha ukládá vrstvy buildu do cache GitHub Actions. Další buildy, kde se změnil jen kód, jsou výrazně rychlejší – pokud máte Dockerfile seřazený správně (viz Dockerfile best practices).

Velká písmena v názvu repozitáře

Název image musí být malými písmeny. github.repository vrací Firma/MojeApp přesně tak, jak je na GitHubu. docker/metadata-action název normalizuje sám, ale pokud název image skládáte ručně, převeďte ho:

      - run: echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"

Viditelnost balíčku

Nový balíček je ve výchozím stavu soukromý (u osobních účtů může záviset na nastavení). Změnit to můžete na stránce balíčku: profil nebo organizace → Packages → balíček → Package settings → Change visibility. Tam také nastavíte, které repozitáře k němu mají přístup.

Stažení image na VPS

Pro soukromý image vytvořte na GitHubu personal access token (classic) jen s oprávněním read:packages a na serveru se přihlaste:

echo "ghp_VAS_TOKEN" | docker login ghcr.io -u vas-github-login --password-stdin

Přihlašovací údaje se uloží do ~/.docker/config.json. Pak v compose.yaml místo build: použijte image::

services:
  web:
    image: ghcr.io/firma/mojeapp:1.4
    restart: unless-stopped
    env_file: .env
    ports:
      - "127.0.0.1:8000:8000"

Aktualizace:

docker compose pull web
docker compose up -d web
docker image prune -f

Tagem 1.4 dostanete automaticky nejnovější patch verzi řady 1.4. Pro plnou kontrolu použijte přesnou verzi 1.4.2 a měňte ji vědomě.

Napojení na automatický deploy

Do workflow můžete přidat job, který po úspěšném pushi image spustí aktualizaci na serveru přes SSH:

  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519
          echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
          ssh deploy@${{ secrets.SSH_HOST }} 'cd /srv/app && docker compose pull && docker compose up -d'

Podrobný postup s nastavením klíčů a omezením přístupu je v článku Automatický deploy na VPS přes GitHub Actions. Pro deploy z main v tomto případě v compose.yaml používejte tag main.

Úklid starých verzí

Každý push přidá nový image se sha- tagem a registr roste. Staré verze smažete ručně na stránce balíčku, nebo automaticky akcí actions/delete-package-versions, která umí ponechat jen posledních N verzí.

Časté chyby

Chyba Příčina
denied: permission_denied při push chybí packages: write, nebo balíček už existuje a repozitář k němu nemá přístup
repository name must be lowercase velká písmena v názvu image
server: unauthorized při pull soukromý image a chybí docker login / token bez read:packages
build je pokaždé pomalý chybí cache-from/cache-to, nebo COPY . . před instalací závislostí