towerops/k8s
Graham McIntire edd64479df
feat: create builder and runtime base images for faster deploys
Instead of installing system packages and tools on every deploy, we now
have two base images that pre-bake all the slow setup steps.

Base Images Created:
1. elixir-builder:latest - Builder stage base
   - hexpm/elixir with build-essential and git pre-installed
   - hex and rebar pre-installed
   - Saves ~40 seconds per deploy

2. debian-runtime:latest - Runtime stage base
   - Debian with all runtime packages pre-installed
   - Locale pre-configured (en_US.UTF-8)
   - /app directory pre-created
   - Saves ~60 seconds per deploy

Main Dockerfile Changes:
- Use elixir-builder:latest instead of hexpm/elixir
- Use debian-runtime:latest instead of debian:trixie-slim
- Remove apt-get install steps (now in base images)
- Remove hex/rebar install (now in builder base)
- Remove locale setup (now in runtime base)

Build System:
- build.sh builds both images with podman/docker auto-detect
- Pushes to both GitLab registry and Docker Hub
- Makefile targets for build/test/push/clean

Total Time Saved Per Deploy: ~100 seconds (1m40s)

Benefits:
- Faster CI/CD builds (no repeated apt-get install)
- Faster local development builds
- Consistent build environment across all deploys
- Security updates centralized in base image rebuilds
2026-01-25 09:29:22 -06:00
..
base-image feat: create builder and runtime base images for faster deploys 2026-01-25 09:29:22 -06:00
certificate.yaml Remove www subdomain from certificate and IngressRoute 2026-01-02 15:06:21 -06:00
deployment.yaml reduce replicas 2026-01-24 15:59:52 -06:00
Dockerfile feat: create builder and runtime base images for faster deploys 2026-01-25 09:29:22 -06:00
ingressroute.yaml Remove www subdomain from certificate and IngressRoute 2026-01-02 15:06:21 -06:00
kustomization.yaml refactor: migrate from etcd to Oban for distributed job coordination 2026-01-24 16:12:27 -06:00
namespace.yaml Add Kubernetes deployment manifests for towerops.net 2026-01-02 14:38:52 -06:00
poddisruptionbudget.yaml Add zero-downtime deployment configuration 2026-01-17 12:44:22 -06:00
README.md Set DEPLOY_TIMESTAMP from GitLab CI instead of pod metadata 2026-01-17 11:17:07 -06:00
service-headless.yaml cluster 2026-01-02 15:29:02 -06:00
service.yaml Add Kubernetes deployment manifests for towerops.net 2026-01-02 14:38:52 -06:00

Kubernetes Deployment

Secrets Management

Secrets are managed directly in the cluster and must be created before deploying the application.

Required secrets in the towerops namespace:

  • gitlab-registry - Docker registry credentials for pulling images
  • towerops-secrets - Application secrets (RELEASE_COOKIE, SECRET_KEY_BASE)
  • towerops-db - Database connection credentials
  • towerops-aws - AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION)

For local development, the project root .envrc is used by direnv.

Deployment Timestamp

The application footer displays the deployment timestamp to track when the current version was deployed. This is automatically set by GitLab CI during deployment:

# GitLab CI sets this during deploy
- kubectl set env deployment/towerops DEPLOY_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") -n towerops

All pods in the deployment share the same timestamp (when the deployment was initiated), regardless of when individual pods were created. This is displayed in the footer as "Last deployed X ago · YYYY-MM-DD HH:MM:SS UTC".

For manual deployments without GitLab CI, set the timestamp:

kubectl set env deployment/towerops DEPLOY_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") -n towerops

Deploying

Apply all resources using kustomize:

kubectl apply -k k8s/

Or individually:

kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/service-headless.yaml
kubectl apply -f k8s/certificate.yaml
kubectl apply -f k8s/ingressroute.yaml