Skip to content

Auto-Deployment (GitOps) Deep Dive

How Auto-Deployment Works (GitOps)

Push an app → CI builds image → Flux detects new tag or PR with values → reconciles Helm/Kustomize to the cluster.

The Magic: FluxCD + Helm/Kustomize

What It Does

graph LR
    A[App CI Build] -->|Push Image| B[GHCR]
    B -->|Tag Update| C[Flux Image Automation]
    C -->|Bump Values| D[Git Repo]
    D -->|Source| E[Flux Controllers]
    E -->|Reconcile| F[Cluster State]

App Packaging Examples

Helm values → K8s Deployment

image:
  repository: ghcr.io/dine-together/frontend
  tag: sha-abcdef
service:
  port: 3000

Becomes:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: web
        image: myapp:latest
        ports:
        - containerPort: 3000

Service + Ingress

# values.yaml
ingress:
  className: nginx
  hosts:
    - host: frontend.staging.dinetogether.co.uk
      paths:
        - path: /
          pathType: Prefix

Creates Service + Ingress managed by the chart.

Volumes → PersistentVolumeClaims

# docker-compose.yml
volumes:
  - postgres_data:/var/lib/postgresql/data

Becomes:

# PersistentVolumeClaim
kind: PersistentVolumeClaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Auto-Deploy Workflow (Flux)

Trigger Mechanism

# In app repository workflow: build and push image
- name: Build and push
  uses: docker/build-push-action@v6
  with:
    push: true
    tags: ghcr.io/dine-together/frontend:sha-${{ github.sha }}

Auto-Deploy Steps

  1. Image built and pushed to GHCR
  2. Flux image automation updates Helm values (or a bot PR bumps tag)
  3. Flux reconciles and applies changes to the cluster

Packaging Guidance

Use golden Helm charts (Django API, Next.js frontend) and configure values for hostnames, resources, probes, and secrets.

Port Detection

# Explicit ports
if 'ports' in config:
    # Use specified ports

# Implicit ports (Next.js/Node.js)
elif 'node' in image or 'next' in image:
    ports = [{'containerPort': 3000}]

# Default web port
else:
    ports = [{'containerPort': 80}]

Network Detection

if 'ingress' in config.get('networks', []):
    # Create public ingress
    # Add to load balancer

Environment Management

Namespace Mapping

Environment Namespace Domain
staging dine-together-staging *.staging.dinetogether.co.uk
production dine-together-production *.dinetogether.co.uk

URL Generation

host = f"{app}.staging.dinetogether.co.uk"  # or production domain

Security Features

  • ImagePullSecrets for GHCR
  • External Secrets or SOPS with Flux for secret management

Network Isolation

  • Internal services: Default network only
  • Public services: Default + Ingress network

Advanced Features

Resource Limits

# docker-compose.yml
deploy:
  resources:
    limits:
      cpus: '0.5'
      memory: 512M

Converts to:

resources:
  limits:
    cpu: 500m
    memory: 512Mi

Health Checks

# docker-compose.yml
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:3000"]
  interval: 30s

Becomes:

livenessProbe:
  httpGet:
    path: /
    port: 3000
  periodSeconds: 30

Replicas

# docker-compose.yml
deploy:
  replicas: 3

Sets:

spec:
  replicas: 3

Debugging GitOps

View Applied Manifests

flux get kustomizations
flux logs -f

Check Conversion Logs

gh run view --log | grep "Convert Docker Compose"

Force Reconcile

flux reconcile kustomization cluster-staging --with-source

Customization

Skip Auto-Deployment

Pause a HelmRelease or Kustomization in Flux.

Custom Ingress Annotations

labels:
  - "traefik.http.routers.myapp.tls=true"
  - "traefik.http.middlewares.myapp.compress=true"

Override Detection

x-app-type: api  # Force API detection
x-port: 8080     # Override port detection

Best Practices

  1. Keep docker-compose.yml Simple
  2. One service per function
  3. Clear service names
  4. Explicit configuration

  5. Use Standard Ports

  6. 3000 for Node.js
  7. 8000 for Django
  8. 80 for nginx

  9. Network Configuration

  10. Add ingress network for public services
  11. Keep databases on default network only

  12. Secrets

  13. Use External Secrets or SOPS; avoid plaintext in Git

Next Steps