Skip to main content

Install Pagifier

Two paths:

  • Local quickstart — a complete platform on your laptop with kind, building the images from source. ~15 minutes, nothing needed but Docker. Best way to evaluate or hack on Pagifier.
  • Production — a real cluster with S3, RDS, and a proper registry.

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.

Pick your cloud

Ready-made values presets live in examples/values/; each file documents the exact identity bindings it needs:

ClusterStorageRegistryIdentityPreset
EKSS3 (storage.provider: s3)ECRIRSA role annotationvalues-eks.yaml
GKEGCS (storage.provider: gcs)Artifact RegistryWorkload Identity annotationvalues-gke.yaml
AKSAzure Blob (storage.provider: azure + azureAccountURL)ACRAzure Workload Identity annotationvalues-aks.yaml
Anywhere elseMinIO / any S3-compatible (storage.endpoint)Harbor/GHCR + registry.pushSecretmounted credentialsthe 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 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.

ValueRecommendation
buildkitPool.enabled=trueShared layer cache — dramatically faster builds
operator.maxConcurrentBuilds=10Protects the pool and registry from herds
prometheusURL=…Enables metric-gated canaries
notifyWebhook=…Slack/Teams deploy notifications
preview.domain=preview.company.comPer-PR preview URLs (wildcard DNS + TLS)
signingSecret=pagifier-cosignImage signing + SLSA provenance
api.tokensSecretRotate tokens by updating the 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

Then hand your developers the getting started page and a token.

Upgrades and rollback (of Pagifier itself)

The API and operator are stateless: 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.