Skip to content

First Deployment

Overview

Ready to deploy your first application? This guide will walk you through deploying an app to your DineTogether infrastructure using GitOps with FluxCD and Helm.

Prerequisites

  • ✅ K3s installed on your server (Server Setup)
  • ✅ FluxCD bootstrapped (GitOps Setup)
  • ✅ GitHub repository for your app
  • ✅ Docker installed locally
  • ✅ GitHub CLI installed (gh)

Step 1: Prepare Your Application

Create a Dockerfile

If you don't have one already:

For Next.js:

FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/node_modules ./node_modules

EXPOSE 3000
CMD ["npm", "start"]

For Django:

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "myapp.wsgi:application", "--bind", "0.0.0.0:8000"]

Step 2: Set Up GitHub Actions

Create .github/workflows/build.yml in your app repository:

name: Build and Push Image

on:
  push:
    branches: [main]
  pull_request:
    types: [opened, synchronize]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=sha,prefix=sha-
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Step 3: Deploy with Helm

DineTogether provides pre-configured Helm charts for common app types.

Create a HelmRelease in this repository under clusters/staging/apps/<your-app>/helmrelease.yaml:

apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: your-app
  namespace: staging
spec:
  interval: 5m
  releaseName: your-app
  chart:
    spec:
      chart: nextjs-app  # or django-app for Django
      sourceRef:
        kind: GitRepository
        name: app-charts
        namespace: flux-system
      version: "*"
  values:
    image:
      repository: ghcr.io/dine-together/your-app
      tag: sha-abc123  # Update with your image tag
      pullPolicy: IfNotPresent

    ingress:
      enabled: true
      className: nginx
      hosts:
        - host: your-app.staging.dinetogether.co.uk
          paths:
            - path: /
              pathType: Prefix
      tls:
        - secretName: your-app-tls
          hosts:
            - your-app.staging.dinetogether.co.uk

    service:
      type: ClusterIP
      port: 3000  # 3000 for Next.js, 8000 for Django

    resources:
      requests:
        cpu: 100m
        memory: 256Mi
      limits:
        cpu: 500m
        memory: 512Mi

    env:
      - name: NODE_ENV
        value: production
      - name: API_URL
        value: https://api.staging.dinetogether.co.uk

    # Reference existing secrets
    secretRefs:
      - name: your-app-secrets  # Create this separately

Option B: Use Kustomize

For more control, create Kubernetes manifests directly with Kustomize.

Create the following structure under apps/your-app/:

apps/your-app/
├── base/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   └── kustomization.yaml
└── overlays/
    ├── staging/
    │   └── kustomization.yaml
    └── production/
        └── kustomization.yaml

Then reference it in clusters/staging/kustomization.yaml:

resources:
  - ../../apps/your-app/overlays/staging

Step 4: Configure Secrets

Create Kubernetes Secrets

# Create secret for your app
kubectl create secret generic your-app-secrets \
  --namespace=staging \
  --from-literal=DATABASE_URL=postgresql://... \
  --from-literal=API_KEY=your-key-here

For Image Pull Secrets (if repository is private)

# Create GitHub token with read:packages permission
# Then create the secret
kubectl create secret docker-registry ghcr-credentials \
  --namespace=staging \
  --docker-server=ghcr.io \
  --docker-username=YOUR_GITHUB_USERNAME \
  --docker-password=YOUR_GITHUB_TOKEN

Step 5: Deploy!

Commit and Push

# Add your HelmRelease file
git add clusters/staging/apps/your-app/helmrelease.yaml

# Commit
git commit -m "Deploy your-app to staging"

# Push to trigger deployment
git push origin main

Monitor Deployment

FluxCD will automatically detect the changes and deploy your app:

# Watch Flux reconciliation
flux get kustomizations --watch

# Check HelmRelease status
flux get helmreleases -n staging

# Watch pods
kubectl get pods -n staging -w

# Check logs
kubectl logs -n staging deployment/your-app

Step 6: Set Up Image Automation (Optional)

To automatically update deployments when new images are pushed:

  1. Create an ImageRepository to scan GHCR:

    apiVersion: image.toolkit.fluxcd.io/v1beta2
    kind: ImageRepository
    metadata:
      name: your-app
      namespace: flux-system
    spec:
      image: ghcr.io/dine-together/your-app
      interval: 1m
    

  2. Create an ImagePolicy to select tags:

    apiVersion: image.toolkit.fluxcd.io/v1beta2
    kind: ImagePolicy
    metadata:
      name: your-app-staging
      namespace: flux-system
    spec:
      imageRepositoryRef:
        name: your-app
      policy:
        semver:
          range: ">=0.0.0"  # or use a specific pattern
    

  3. Update your HelmRelease to use the marker:

    values:
      image:
        tag: sha-abc123  # {"$imagepolicy": "flux-system:your-app-staging:tag"}
    

Now Flux will automatically update the tag when new images are pushed!

Verify Deployment

Check Application Status

# Get all resources
kubectl get all -n staging -l app=your-app

# Check ingress
kubectl get ingress -n staging

Access Your Application

Your app will be available at:

https://your-app.staging.dinetogether.co.uk

SSL certificates are automatically provisioned via cert-manager.

Troubleshooting

Pod Not Starting

# Check pod status
kubectl describe pod -n staging -l app=your-app

# Check events
kubectl get events -n staging --sort-by='.lastTimestamp'

Image Pull Errors

# Verify image exists
docker pull ghcr.io/dine-together/your-app:sha-abc123

# Check image pull secrets
kubectl get secret ghcr-credentials -n staging

Flux Issues

# Check Flux logs
flux logs --follow

# Force reconciliation
flux reconcile helmrelease your-app -n staging

Next Steps

Production Deployment

  1. Create clusters/production/apps/your-app/helmrelease.yaml
  2. Update values for production (replicas, resources, domains)
  3. Use production secrets
  4. Enable monitoring and alerts

Add Monitoring

Your app automatically gets monitoring if you add Prometheus annotations:

spec:
  template:
    metadata:
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "3000"
        prometheus.io/path: "/metrics"

Configure CI/CD

  • Set up staging deployment on merge to main
  • Add production deployment with manual approval
  • Configure automated testing before deployment

Congratulations! 🎉

You've successfully deployed your application using GitOps!

What You've Accomplished

  • ✅ Built and pushed Docker image to GHCR
  • ✅ Created HelmRelease for GitOps deployment
  • ✅ Deployed to Kubernetes via FluxCD
  • ✅ Automatic SSL certificate provisioning
  • ✅ Public HTTPS access with monitoring

Key Points to Remember

  1. GitOps: All changes go through Git
  2. Helm Charts: Use golden charts for standard apps
  3. Image Automation: Optional automatic updates
  4. Monitoring: Built-in Prometheus/Grafana support

Need Help?

Happy deploying! 🚀