Install Pagifier
This is the platform-engineer side: cluster, persistence, registry, identity, and the public API. Developers can stay over in the developer guide unless they are running a laptop sandbox.
Two ways inβpick your adventure:
- On your laptop π§ͺ β the whole platform on a kind cluster, images built from source, ~15 minutes, nothing needed but Docker. The best way to kick the tires.
- For real βοΈ β a proper cluster, proper storage, proper registry. EKS, GKE, AKS, or anything that runs Kubernetes.
Local quickstart (laptop)β
Prerequisites: Docker, kind, kubectl, Helm, and the AWS CLI (only used to talk to local MinIO). Clone the repo first:
git clone https://github.com/kcloudev/pagifier-dev.git && cd pagifier-dev
1. Cluster + local registryβ
./hack/quickstart/kind-with-registry.sh
This creates a kind cluster named pagifier and a local Docker
registry at localhost:5001 that's reachable from inside the cluster
too β that's where your built platform images and the app images
built by Pagifier will live.
Install an ingress controller:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
kubectl -n ingress-nginx wait --for=condition=Ready pod -l app.kubernetes.io/component=controller --timeout=180s
2. Dependencies: MinIO + PostgreSQLβ
kubectl apply -f hack/quickstart/minio.yaml -f hack/quickstart/postgres.yaml
kubectl -n pagifier-deps wait --for=condition=Available deploy --all --timeout=180s
MinIO gives you both S3 buckets (pagifier-artifacts,
pagifier-platform β a Job creates them); Postgres holds deploy
history. Both are throwaway dev instances.
Seed the platform configuration (templates, profiles, runtime images) and the database schema:
# platform config β MinIO
kubectl -n pagifier-deps port-forward svc/minio 9000:9000 >/dev/null 2>&1 &
AWS_ACCESS_KEY_ID=pagifier AWS_SECRET_ACCESS_KEY=pagifier-dev-secret \
aws --endpoint-url http://localhost:9000 s3 sync examples/platform-config/ s3://pagifier-platform/
# schema β Postgres
kubectl -n pagifier-deps exec deploy/postgres -- psql -U pagifier -d pagifier \
< docs/db/schema.sql
3. Build your own imagesβ
make images REGISTRY=localhost:5001/pagifier VERSION=dev
docker push localhost:5001/pagifier/api:dev
docker push localhost:5001/pagifier/operator:dev
docker push localhost:5001/pagifier/builder:dev
Three images from source: the API server, the operator, and the builder
(which carries buildctl and cosign into build pods).
4. Install Pagifierβ
kubectl create namespace pagifier-system
kubectl -n pagifier-system create secret generic pagifier-api-tokens \
--from-literal=tokens='dev-token:acme'
helm install pagifier charts/pagifier -n pagifier-system \
--set image.api=localhost:5001/pagifier/api:dev \
--set image.operator=localhost:5001/pagifier/operator:dev \
--set image.builder=localhost:5001/pagifier/builder:dev \
--set registry.url=pagifier-registry:5000/apps \
--set registry.insecure=true \
--set storage.endpoint=http://minio.pagifier-deps.svc:9000 \
--set storage.artifactBucket=pagifier-artifacts \
--set storage.configBucket=pagifier-platform \
--set api.databaseURL=postgres://pagifier:pagifier-dev-secret@postgres.pagifier-deps.svc:5432/pagifier
kubectl -n pagifier-system set env deploy/pagifier-api deploy/pagifier-operator \
AWS_ACCESS_KEY_ID=pagifier AWS_SECRET_ACCESS_KEY=pagifier-dev-secret
kubectl -n pagifier-system wait --for=condition=Available deploy --all --timeout=180s
Two values matter for local: registry.url points app-image pushes at
the in-cluster name of your local registry, and registry.insecure=true
lets BuildKit push over plain HTTP (never use it beyond a laptop).
5. Deploy somethingβ
kubectl -n pagifier-system port-forward svc/pagifier-api 8443:80 >/dev/null 2>&1 &
cd examples/minimal
sed -i '' 's/environment = "prod"/environment = "dev"/; s/my-app.company.com/my-app.localtest.me:8080/' pagifier.toml
zip -q app.zip pagifier.toml # any static content works; the template builds real apps
curl -H "Authorization: Bearer dev-token" \
-F config=@pagifier.toml -F archive=@app.zip \
"http://localhost:8443/v1/applications/my-app/environments/dev/deployments?wait=true"
Watch the build stream, then open http://my-app.localtest.me:8080
(localtest.me resolves to 127.0.0.1; port 8080 is the kind ingress
mapping). The console is at http://localhost:8443/console β paste
dev-token.
Tear it all down with kind delete cluster --name pagifier.
Production installβ
You provide: Kubernetes β₯ 1.29 with an nginx ingress controller, object storage, PostgreSQL, and an OCI registry. Optional integrations β External Secrets Operator, KEDA, Prometheus, external-dns, a Gateway β each enables the matching feature.
Cloud user? Pick yours πβ
Ready-made values presets live in examples/values/ β each one has the
exact IAM incantations written down so you don't have to spelunk cloud
docs:
| Cluster | Storage | Registry | Identity | Preset |
|---|---|---|---|---|
| EKS | S3 (storage.provider: s3) | ECR | IRSA role annotation | values-eks.yaml |
| GKE | GCS (storage.provider: gcs) | Artifact Registry | Workload Identity annotation | values-gke.yaml |
| AKS | Azure Blob (storage.provider: azure + azureAccountURL) | ACR | Azure Workload Identity annotation | values-aks.yaml |
| Anywhere else | MinIO / any S3-compatible (storage.endpoint) | Harbor/GHCR + registry.pushSecret | mounted credentials | the quickstart flow above |
Credentials are always ambient β Pagifier never stores cloud keys. The per-component access matrix (API reads config + writes artifacts; build jobs read artifacts only; the operator touches no storage) is identical on every cloud; see S3 configuration.
DNS on every cloud works through
external-dns: install
it with a service-account identity scoped to your zone (Route53 /
Cloud DNS / Azure DNS per the preset comments) and records for every
ingress host appear and disappear automatically. Projects tune records
with the dns block (ttl, target).
CDN per cloud is a platform document: publish
cdn/<provider>.yaml (seed examples for CloudFront, Cloud CDN, Front
Door, and Cloudflare ship in examples/platform-config/cdn/) and
projects opt in with one line β cdn = "cloudfront". The definitions
pair with dns.target so the hostname resolves to the edge, and with
origin-protection headers so only the CDN reaches the cluster.
1. Imagesβ
Use published images, or build and push your own:
make push REGISTRY=ghcr.io/yourorg/pagifier VERSION=1.0.0
2. Storage and databaseβ
aws s3 mb s3://pagifier-artifacts && aws s3 mb s3://pagifier-platform
make sync-platform-config CONFIG_BUCKET=pagifier-platform # then customize!
psql "$DATABASE_URL" -f docs/db/schema.sql
Review examples/platform-config/ before syncing β the environment
profiles and templates in it become your platform's behavior. See
S3 configuration for what every document
does.
3. Installβ
kubectl create namespace pagifier-system
kubectl -n pagifier-system create secret generic pagifier-api-tokens \
--from-literal=tokens='TEAM_A_TOKEN:team-a,TEAM_B_TOKEN:team-b'
helm install pagifier charts/pagifier -n pagifier-system \
--set image.api=ghcr.io/yourorg/pagifier/api:1.0.0 \
--set image.operator=ghcr.io/yourorg/pagifier/operator:1.0.0 \
--set image.builder=ghcr.io/yourorg/pagifier/builder:1.0.0 \
--set registry.url=123456789.dkr.ecr.us-east-1.amazonaws.com/pagifier \
--set storage.artifactBucket=pagifier-artifacts \
--set storage.configBucket=pagifier-platform \
--set storage.region=us-east-1 \
--set api.databaseURL="$DATABASE_URL" \
--set gitObserver.publicURL=https://pagifier.company.com \
--set serviceAccount.annotations."eks\.amazonaws\.com/role-arn"=arn:aws:iam::123456789:role/pagifier
On EKS, grant S3/ECR through IRSA (the annotation above); elsewhere use
registry.pushSecret and mounted credentials. The exact IAM policies
per component are in
S3 configuration.
4. Recommended production valuesβ
| Value | Recommendation |
|---|---|
buildkitPool.enabled=true | Shared layer cache β dramatically faster builds |
operator.maxConcurrentBuilds=10 | Protects the pool and registry from herds |
prometheusURL=β¦ | Enables metric-gated canaries |
notifyWebhook=β¦ | Slack/Teams deploy notifications |
preview.domain=preview.company.com | Per-PR preview URLs (wildcard DNS + TLS) |
signingSecret=pagifier-cosign | Image signing + SLSA provenance |
api.tokensSecret | Rotate tokens by updating the secret |
gitObserver.publicURL | Enables automatic GitHub/GitLab webhook installation |
gitObserver.secretName | Retain and back up this credential-encryption Secret |
5. Verifyβ
kubectl -n pagifier-system get pods
curl https://pagifier.company.com/healthz # ok
curl -H "Authorization: Bearer $TOKEN" --data-binary @pagifier.toml \
https://pagifier.company.com/v1/validate
Next, connect Git, replace everyday use of bootstrap admin tokens with scoped tokens, and publish your templates and profiles. Then hand developers the developer guide, the connection IDs they may use, and a narrow token.
Upgrades and rollback (of Pagifier itself)β
The API and operator are stateless when PostgreSQL is configured:
helm upgrade / helm rollback.
Leader election keeps operator upgrades non-disruptive; in-flight
builds survive restarts (Jobs are owned by Releases). Because releases
embed their resolved configuration, upgrading Pagifier never changes
running applications. Apply the latest docs/db/schema.sql before
upgrading the API. The Helm-managed Git credential encryption key is
annotated helm.sh/resource-policy: keep; do not delete it while encrypted
connections exist.