Kubernetes Deployment for App Portal (Backstage)
Automated deployment of App Portal (Backstage) to Kubernetes using environment variable substitution.
Quick Start
# Source your environment variables (from portal-workspace)
source ../.env.rancher-desktop # or your cluster-specific env file
# Deploy to cluster
./kubernetes-deploy.sh
# Or dry-run (generate manifests only)
./kubernetes-deploy.sh --dry-run
Prerequisites
-
Kubernetes cluster with:
- NGINX Ingress Controller
- cert-manager with ClusterIssuer
letsencrypt-prod(for TLS) - ExternalDNS configured (optional, for automatic DNS management)
-
Environment variables set:
APP_HOSTNAME- Application hostname (e.g., app.example.com)DOCKER_IMAGE- Docker image to deployAUTH_GITHUB_APP_ID- GitHub App IDAUTH_GITHUB_CLIENT_ID- GitHub App Client IDAUTH_GITHUB_CLIENT_SECRET- GitHub App Client SecretAUTH_GITHUB_APP_INSTALLATION_ID- GitHub App Installation IDGITHUB_APP_PRIVATE_KEY- GitHub App private key (PEM format)
-
Docker image built and pushed:
./build-backend.sh
./docker-build.sh
./docker-push.sh
Deployment Script
The kubernetes-deploy.sh script:
- Validates required environment variables
- Generates Kubernetes manifests from templates in
examples/kubernetes/base/ - Applies manifests to your cluster (or dry-run)
- Constructs APP_BASE_URL and BACKEND_BASE_URL from APP_HOSTNAME if not set
Usage
# Deploy to cluster (default behavior)
./kubernetes-deploy.sh
# Dry run - only generate manifests
./kubernetes-deploy.sh --dry-run
# Custom output directory
./kubernetes-deploy.sh -o ./my-manifests
# Show help
./kubernetes-deploy.sh --help
Generated Files
The script creates these manifests in kubernetes-manifests/:
namespace.yaml- app-portal namespacesecret.yaml- GitHub App credentials and configdeployment.yaml- Application deploymentservice.yaml- ClusterIP serviceingress.yaml- Ingress with TLS and ExternalDNS annotationskustomization.yaml- Kustomize configuration
Manual Deployment
If you prefer manual deployment:
# Export required variables
export APP_HOSTNAME=app.example.com
export DOCKER_IMAGE=ghcr.io/org/app:v1.0.0
# ... export other required vars ...
# Generate manifests
mkdir -p kubernetes-manifests
for file in examples/kubernetes/base/*.yaml; do
envsubst < "$file" > "kubernetes-manifests/$(basename $file)"
done
# Apply to cluster
kubectl apply -f kubernetes-manifests/
Verify Deployment
# Check pod status
kubectl get pods -n app-portal
# View logs
kubectl logs -n app-portal -l app.kubernetes.io/name=app-portal
# Check ingress
kubectl get ingress -n app-portal
# Access the application
curl https://${APP_HOSTNAME}
Troubleshooting
Pod not starting
kubectl describe pod -n app-portal -l app.kubernetes.io/name=app-portal
kubectl logs -n app-portal -l app.kubernetes.io/name=app-portal --previous
GitHub App authentication issues
- Verify environment variables are set correctly
- Check private key format (should include header/footer)
- Ensure Installation ID matches your GitHub organization
DNS/Ingress issues
- Verify DNS points to ingress controller IP
- For ExternalDNS:
kubectl logs -n external-dns deployment/external-dns - Check ingress:
kubectl describe ingress -n app-portal app-portal
Security Notes
- Never commit real secrets to the repository
- Environment variables should be sourced from secure storage
- GitHub private key is mounted read-only with 0400 permissions
- Consider using Sealed Secrets or External Secrets Operator for production
Production Considerations
- Database: Switch from SQLite to PostgreSQL
- High Availability: Increase replica count
- Resources: Adjust CPU/memory limits based on load
- Monitoring: Add Prometheus metrics
- Backup: Implement persistent data backup strategy