Post

Portainer

Portainer

Portainer

Web UI for managing the k3s cluster. Runs as portainer in the default namespace, deployed and kept in sync by ArgoCD.

Stack

ItemValue
Imageportainer/portainer-ce:latest
Namespacedefault
Replicas1 (strategy: Recreate)
Ports9000 (HTTP UI), 8000 (Edge agent)
StoragePVC portainer-data, 2Gi, local-path storage class
ServiceAccountportainer-sa-clusteradmin → ClusterRoleBinding to cluster-admin
GitOpsArgoCD Application — repo Portainer.git, branch k3s
Reloaderreloader.stakater.com/auto: "true"

RBAC

Portainer runs under a dedicated ServiceAccount bound to the built-in cluster-admin ClusterRole via portainer-crb-clusteradmin. This gives Portainer full read/write access to every resource in every namespace — required for it to manage the whole cluster through its UI, not just the default namespace it’s deployed in.

Networking

  • Service/portainer — ClusterIP, ports 9000 and 8000, selector app=portainer.
  • IngressRoute/portainer — Traefik, TLS via local-example-com-tls:
    • www.portainer.prod-k3s.homelab.example → portainer:9000 (no middleware)
    • portainer.prod-k3s.homelab.example → portainer:9000 (default-headers middleware)

The container runs with --http-enabled since TLS is terminated at Traefik, not by Portainer itself.

Storage

A single 2Gi local-path PVC (portainer-data) mounted at /data holds Portainer’s own database (users, endpoints, settings) — this is node-local storage, so the pod is implicitly tied to whichever node it first schedules on.

Deploy

1
kubectl apply -f serviceaccount.yaml -f pvc.yaml -f deployment.yaml -f service.yaml -f ingress.yaml

In practice this isn’t applied by hand — ArgoCD watches the k3s branch of the Portainer repo and syncs automatically.

This post is licensed under CC BY 4.0 by the author.