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 pull → helm 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) :
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 :
- On GitHub, go to your org : https://github.com/orgs/AKKO-p/packages
- Select a package (e.g.
akko-postgres) - Package settings → Manage Actions access → make sure your repo has Read access.
- 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 :
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: trueto previewhelm templatewithout applying - Post-Deploy Tests :
full: trueto 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 :
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-