Files
turbovault-app/INITIAL_DEPLOYMENT.md
Ryan Kazokas 69993a3bf5 Deploy to production: GitHub Actions + ghcr.io + Kubernetes
- Switch from Gitea to GitHub Container Registry (ghcr.io)
- Add GitHub Actions workflow with Tailscale connectivity
- Update k8s manifests for cloud nodes and Traefik ingress
- Configure for turbo.kazcloud.dev domain
- Test deployment with home page text change
2026-03-29 08:46:27 -04:00

6.5 KiB

Initial Deployment Guide

Follow these steps to deploy TurboVault to Kubernetes for the first time.

Prerequisites

  • Code pushed to GitHub
  • PostgreSQL database ready (host, user, password)
  • kubectl configured for k3s cluster (100.101.31.99:6443)
  • Docker installed locally
  • Gitea account at gitea.kazcloud.dev

Step 1: Build and Push Initial Image

Since GitHub Actions hasn't run yet, build the first image manually:

# Build image
docker build -t gitea.kazcloud.dev/ryan/turbovault-app:v1.0.0 .

# Login to Gitea registry
docker login gitea.kazcloud.dev
# Username: ryankazokas
# Password: <your-gitea-token>

# Push image
docker push gitea.kazcloud.dev/ryan/turbovault-app:v1.0.0

# Also tag as latest
docker tag gitea.kazcloud.dev/ryan/turbovault-app:v1.0.0 \
           gitea.kazcloud.dev/ryan/turbovault-app:latest
docker push gitea.kazcloud.dev/ryan/turbovault-app:latest

Verify: Check Gitea packages at gitea.kazcloud.dev/ryankazokas/-/packages


Step 2: Configure Kubernetes Secrets

# Copy template
cp k8s/secrets.yaml.example k8s/secrets.yaml

# Generate Rails secret key
rails secret
# Copy the output

# Edit secrets file
nano k8s/secrets.yaml

Add these values in k8s/secrets.yaml:

apiVersion: v1
kind: Secret
metadata:
  name: turbovault-secrets
  namespace: turbovault
type: Opaque
stringData:
  SECRET_KEY_BASE: "<paste-output-from-rails-secret>"
  DATABASE_PASSWORD: "your-postgres-password"
  
  # Optional: IGDB integration
  IGDB_CLIENT_ID: "your-igdb-client-id"
  IGDB_CLIENT_SECRET: "your-igdb-client-secret"
  
  # Optional: Email (for password resets)
  SMTP_ADDRESS: "smtp.example.com"
  SMTP_PORT: "587"
  SMTP_USERNAME: "user@example.com"
  SMTP_PASSWORD: "smtp-password"

Step 3: Configure Database Connection

Edit k8s/configmap.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: turbovault-config
  namespace: turbovault
data:
  RAILS_ENV: "production"
  DATABASE_HOST: "your-postgres-host"
  DATABASE_NAME: "turbovault_production"
  DATABASE_USERNAME: "turbovault"
  RAILS_LOG_TO_STDOUT: "true"
  RAILS_SERVE_STATIC_FILES: "true"

Replace:

  • your-postgres-host - Your PostgreSQL server hostname/IP
  • Database name and username if different

Step 4: Deploy to Kubernetes

Run the automated deployment script:

./scripts/deploy-k8s.sh

When prompted for registry credentials:

  • Registry: gitea.kazcloud.dev
  • Username: ryankazokas
  • Password: <your-gitea-token>

The script will:

  1. Create namespace
  2. Apply configmap
  3. Apply secrets
  4. Run database migration job
  5. Deploy application
  6. Create service
  7. Create ingress

Step 5: Verify Deployment

# Check all resources
kubectl get all -n turbovault

# Check pods are running
kubectl get pods -n turbovault

# View logs
kubectl logs -f -l app=turbovault -n turbovault

# Check migration job completed
kubectl logs job/turbovault-migrate -n turbovault

Expected output:

NAME                              READY   STATUS    RESTARTS   AGE
pod/turbovault-xxxxxxxxxx-xxxxx   1/1     Running   0          2m
pod/turbovault-xxxxxxxxxx-xxxxx   1/1     Running   0          2m

Step 6: Access Application

Option A: Port Forward (Testing)

kubectl port-forward svc/turbovault-service 3000:80 -n turbovault

Visit: http://localhost:3000

Option B: Ingress (Production)

Edit k8s/ingress.yaml with your domain and apply:

kubectl apply -f k8s/ingress.yaml

Visit: https://your-domain.com


Step 7: Configure GitHub Secrets

Now set up automated deployments for future updates.

See docs/GITHUB_SECRETS.md for detailed instructions.

Required secrets (add at https://github.com/ryankazokas/turbovault-app/settings/secrets/actions):

  1. GITEA_USERNAME - Your Gitea username
  2. GITEA_TOKEN - Gitea access token (Settings → Applications)
  3. TAILSCALE_CLIENT_ID - Tailscale OAuth client ID
  4. TAILSCALE_CLIENT_SECRET - Tailscale OAuth client secret
  5. KUBECONFIG - Base64-encoded kubeconfig (cat ~/.kube/config | base64 -w 0)

Step 8: Test Automated Deployment

After GitHub secrets are configured:

# Create a test tag
git tag v1.0.1
git push origin v1.0.1

Watch at: https://github.com/ryankazokas/turbovault-app/actions

GitHub Actions will:

  1. Build Docker image
  2. Push to Gitea registry
  3. Connect via Tailscale
  4. Deploy to Kubernetes

Troubleshooting

Pods in CrashLoopBackOff

# View logs
kubectl logs -l app=turbovault -n turbovault

# Common issues:
# - Database connection failed (check configmap/secrets)
# - Missing SECRET_KEY_BASE (check secrets)
# - Migration not run (check migration job logs)

Migration Job Failed

# View migration logs
kubectl logs job/turbovault-migrate -n turbovault

# Re-run migration
kubectl delete job turbovault-migrate -n turbovault
kubectl apply -f k8s/migrate-job.yaml

Can't Pull Image

# Check image pull secret
kubectl get secrets -n turbovault

# Re-run deploy script to create secret
./scripts/deploy-k8s.sh

Database Connection Failed

# Test from pod
kubectl exec -it deployment/turbovault -n turbovault -- \
  rails runner "puts ActiveRecord::Base.connection.execute('SELECT 1').first"

# Check environment variables
kubectl exec -it deployment/turbovault -n turbovault -- env | grep DATABASE

Next Steps

After successful initial deployment:

  1. Application is running in Kubernetes
  2. GitHub Actions configured for automated deployments
  3. 🚀 Daily workflow: Just push tags to deploy!
# Make changes
git add .
git commit -m "Feature: new functionality"
git push

# Deploy
git tag v1.0.2
git push origin v1.0.2

# GitHub Actions automatically builds and deploys! ✅

Quick Reference

# View status
kubectl get all -n turbovault

# View logs
kubectl logs -f -l app=turbovault -n turbovault

# Restart deployment
kubectl rollout restart deployment/turbovault -n turbovault

# Rollback deployment
kubectl rollout undo deployment/turbovault -n turbovault

# Delete everything (start over)
kubectl delete namespace turbovault

Files You Modified

Keep these files safe (they're gitignored):

  • k8s/secrets.yaml - Contains sensitive data (SECRET_KEY_BASE, passwords)
  • ~/.kube/config - Your Kubernetes access

DO NOT commit these to git!


Need help? Check logs first:

kubectl describe pod -l app=turbovault -n turbovault
kubectl logs -l app=turbovault -n turbovault