6.4 KiB
Build Docker images on k3s with Gitea Actions → push to Harbor
This repository contains a complete, reproducible configuration that lets you:
- Run a Gitea Actions runner (
act_runner) on a k3s node. - Build Docker/OCI container images inside the CI pipeline using a
Docker-in-Docker (DinD) sidecar (so plain
docker build/docker pushwork). - Push the built images to a Harbor registry.
The whole pipeline is driven by Gitea Actions (Gitea's built-in, GitHub-Actions-compatible CI/CD), so no external CI system (Jenkins/Drone/etc.) is required.
┌──────────────┐ registers ┌─────────────────────────────────────┐
│ Gitea │◄────────────────│ act_runner (Deployment on k3s) │
│ (Actions) │ job dispatch │ ┌───────────────┐ ┌──────────────┐ │
│ │────────────────►│ │ act_runner │ │ dockerd │ │
└──────────────┘ │ │ container │─►│ (DinD side- │ │
▲ │ └───────────────┘ │ car, │ │
│ git push │ docker build │ privileged)│ │
┌────┴─────┐ │ docker push └──────────────┘ │
│ developer│ └───────────────────────────┬─────────┘
└──────────┘ │ push image
▼
┌──────────────────┐
│ Harbor registry │
└──────────────────┘
Repository layout
| File | Purpose |
|---|---|
| k3s/00-namespace.yaml | Dedicated gitea-runner namespace. |
| k3s/10-runner-secret.yaml.example | Gitea registration token secret (copy & fill in). |
| k3s/20-runner-config.yaml | act_runner config (config.yaml) as a ConfigMap. |
| k3s/30-runner-deployment.yaml | The act_runner + DinD Deployment. |
| k3s/40-harbor-secret.yaml.example | Optional cluster-side Harbor pull secret. |
| workflows/build-and-push.yaml | Example Gitea Actions workflow (put in your app repo). |
| examples/Dockerfile | Minimal sample image to build. |
| scripts/deploy.sh | Helper to apply all manifests. |
| .gitea/workflows/build-and-push.yaml | Same workflow, pre-placed so this repo self-builds. |
Prerequisites
- A running k3s cluster and
kubectlaccess to it (~/.kube/configor/etc/rancher/k3s/k3s.yaml). - A reachable Gitea instance (>= 1.20) with Actions enabled
(
[actions] ENABLED = truein Gitea'sapp.ini). - A running Harbor registry and a robot account (recommended) with
pushpermission on the target project.
Step 1 — Enable Gitea Actions (on the Gitea server)
In Gitea's app.ini:
[actions]
ENABLED = true
Restart Gitea. Then get a runner registration token:
- Instance level: Site Administration → Actions → Runners → Create new Runner → copy the token.
- Org/Repo level: Settings → Actions → Runners → Create new Runner.
Step 2 — Create the runner registration secret
cp k3s/10-runner-secret.yaml.example k3s/10-runner-secret.yaml
# edit k3s/10-runner-secret.yaml and paste your GITEA_INSTANCE_URL + token
kubectl apply -f k3s/10-runner-secret.yaml
Step 3 — Deploy the runner on k3s
./scripts/deploy.sh
# or manually:
kubectl apply -f k3s/00-namespace.yaml
kubectl apply -f k3s/20-runner-config.yaml
kubectl apply -f k3s/30-runner-deployment.yaml
Verify the runner appears online in Gitea (Actions → Runners) and that both containers are ready:
kubectl -n gitea-runner rollout status deploy/act-runner
kubectl -n gitea-runner get pods
Step 4 — Configure Harbor credentials for the pipeline
In your Gitea repository (or org) → Settings → Actions → Secrets, add:
| Secret name | Value |
|---|---|
HARBOR_REGISTRY |
e.g. harbor.example.com |
HARBOR_USERNAME |
Harbor user / robot account, e.g. robot$ci |
HARBOR_PASSWORD |
the robot account token / password |
Using a Harbor robot account (Project → Robot Accounts) instead of a personal login is strongly recommended.
Step 5 — Add the workflow to your app repo
Copy workflows/build-and-push.yaml into your application repository at
.gitea/workflows/build-and-push.yaml, commit, and push. Gitea will dispatch the job to your
k3s runner, which builds the image via DinD and pushes it to Harbor.
How image building works (no Docker on k3s needed)
k3s uses containerd, not Docker, and has no image-build capability. This setup therefore runs a
dockerd DinD sidecar next to act_runner. The runner container talks to it over
tcp://localhost:2376 (TLS), so inside jobs the standard docker CLI works transparently —
docker build, docker login, docker push all behave like on a normal Docker host.
If you prefer a rootless / non-privileged approach (Kaniko or Buildah), see the "Alternative: rootless builds" section at the bottom of workflows/build-and-push.yaml.
Security notes
- The DinD sidecar requires
privileged: true. Keep this runner in its own namespace and, ideally, on a dedicated node (see thenodeSelector/tolerationscomments in the Deployment). - Prefer Harbor robot accounts with the least privilege (push to one project only).
- Store all credentials as Kubernetes/Gitea secrets — never commit the filled-in
*.yaml(non-.example) files. See .gitignore.