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.
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í |