Skip to content

CI/CD with GitHub Actions

AKKO ships four GitHub Actions workflows that turn a simple git push on main into a full build → push → deploy → test pipeline on the production server. All workflows run on GitHub-hosted runners (plan Free, 2000 min/month for private repos) and cost nothing.

The four workflows

Workflow Trigger Duration Role
Helm Lint Pull Request, push on non-main branches ~3 min helm lint + helm template --debug across all sub-charts
Build Images Push main (if docker/, branding/, docs/ changed) ~15 min (parallel) Build 16 custom images + push to ghcr.io/akko-p/akko-*:2026.04
Deploy to Netcup Push main (if helm/ changed) or after Build Images ~10 min SSH → git pullhelm upgrade → wait rollouts
Post-Deploy Tests After Deploy success ~10 min SSH → tests/run-all.sh --fast → fail build if any test fails

Full cycle on a typical PR merge : ~25 min end-to-end, 100% automated.


One-time setup

1. GitHub Secrets (repo Settings → Secrets and variables → Actions)

Add three secrets :

Name Value How to generate
NETCUP_HOST Public IP or hostname of the Netcup server (e.g. 159.195.77.208) Copy from Netcup panel
NETCUP_USER SSH user (e.g. root) Already known
NETCUP_SSH_KEY Private SSH key ed25519 (content of ~/.ssh/github_actions_netcup) See step 2

2. Generate a dedicated SSH key for GitHub Actions

On any machine with SSH access to Netcup :

# Generate a fresh key pair (no passphrase — CI can't interactively unlock)
ssh-keygen -t ed25519 -C "github-actions-akko" -f ~/.ssh/github_actions_netcup -N ''

# Copy the public key to Netcup (adds it to ~/.ssh/authorized_keys)
ssh-copy-id -i ~/.ssh/github_actions_netcup.pub root@159.195.77.208

# Verify it works
ssh -i ~/.ssh/github_actions_netcup root@159.195.77.208 "echo 'OK'"

Then copy the private key content (the file without .pub) :

cat ~/.ssh/github_actions_netcup

Paste the entire output (including the -----BEGIN OPENSSH PRIVATE KEY----- / -----END OPENSSH PRIVATE KEY----- lines) into the GitHub secret NETCUP_SSH_KEY.

3. GHCR permissions (automatic)

Images are pushed to ghcr.io/akko-p/akko-<name>. The built-in GITHUB_TOKEN has the right scope when permissions: packages: write is declared in the workflow (already done in build-images.yml).

On first push, the packages are private by default. To let the cluster pull them :

  1. On GitHub, go to your org : https://github.com/orgs/AKKO-p/packages
  2. Select a package (e.g. akko-postgres)
  3. Package settings → Manage Actions access → make sure your repo has Read access.
  4. For the Netcup cluster to pull : create an imagePullSecret. See next step.

4. Configure Netcup to pull from GHCR

Create a Personal Access Token (classic) with read:packages scope : https://github.com/settings/tokens/new?scopes=read:packages

On Netcup :

kubectl create secret docker-registry ghcr-auth -n akko \
  --docker-server=ghcr.io \
  --docker-username=<your-github-username> \
  --docker-password=<your-pat-token>

Reference it in helm/examples/values-netcup.yaml :

global:
  image:
    registry: "ghcr.io/akko-p"
    pullSecrets:
      - name: ghcr-auth

After a helm upgrade, all pods pull from GHCR using the secret.


How the workflows chain

graph LR
    A[Push main] --> B{Changed path?}
    B -->|docker/* | branding/*| C[Build Images]
    B -->|helm/*| D[Deploy to Netcup]
    C --> D
    D --> E[Post-Deploy Tests]
    E --> F[GitHub Status ✅/❌]
    A2[PR opened] --> G[Helm Lint]
    G --> H[Merge allowed / blocked]

Manual trigger

Any workflow can be triggered manually from Actions → Select workflow → Run workflow :

  • Build Images : rebuild everything even without code change (useful after CVE patch)
  • Deploy to Netcup : dry_run: true to preview helm template without applying
  • Post-Deploy Tests : full: true to run the complete 45-min suite instead of fast (10 min)

Rollback on failure

If Deploy to Netcup or Post-Deploy Tests fails, the previous Helm revision is still active (Helm upgrades are atomic). Manual rollback :

ssh root@159.195.77.208
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
helm history akko -n akko
helm rollback akko <previous-revision> -n akko

Automated rollback on test failure is a planned enhancement (Plan prod-ready : repo privé akko-technical-map/roadmap/plan-prod-ready.md).


Cost & limits

Plan Free (private repo) : - 2000 CI minutes / month on Linux runners - GHCR storage 500 MB free, then 0.25$/GB/month (about 2 GB stored for AKKO = ~0.50$/month at worst, most likely covered by the free tier) - GHCR bandwidth unlimited from GitHub Actions, 1 GB/day outside

Typical monthly consumption : - 20 pushes on main × 25 min = 500 min (25% of quota) - Remaining 1500 min for PRs, manual runs, retries

No hard limit blocks production use.


Troubleshooting

permission denied when pushing to GHCR

Check that the workflow has permissions: packages: write. It's already declared in build-images.yml but a custom workflow may forget it.

Host key verification failed on SSH

The ssh-keyscan step may have missed the host. Run once manually from an existing trusted machine :

ssh-keyscan -H 159.195.77.208 | base64

Store the output in a new secret NETCUP_KNOWN_HOSTS and inject it in the workflow — bypass dynamic keyscan.

helm upgrade failed: cannot patch PVC

Drift between values-netcup.yaml PVC sizes and actual PVC. Fix in values-netcup.yaml and re-push (cf. DEPLOYMENT.md Troubleshooting).

Tests fail but pods look healthy

Check which test failed in the artifact : https://github.com/AKKO-p/AKKO/actions → run → tests-log- artifact → download → inspect.