Some checks failed
Deploy to Production / Build and Deploy (push) Has been cancelled
|
||
---|---|---|
.gitea/workflows | ||
chart | ||
docs | ||
frontend | ||
nginx | ||
src | ||
test | ||
.env.example | ||
.eslintrc.json | ||
.gitignore | ||
.prettierrc.json | ||
architecture.excalidraw | ||
docker-compose.dev.yml | ||
docker-compose.yml | ||
Dockerfile | ||
jest.config.js | ||
mikro-orm.config.ts | ||
package-lock.json | ||
package.json | ||
README.md | ||
trigger.txt | ||
tsconfig.json |
⚡️ Fusero App Boilerplate
A full-stack application boilerplate with a React frontend and Node.js backend — powered by Fastify, Vite, PostgreSQL, Docker, and optional Kubernetes & Helm support. Built for modern dev workflows and AI-powered backend endpoint generation.
📚 Table of Contents
- ⚡️ Fusero App Boilerplate
- 📚 Table of Contents
- 📁 Project Structure
- ⚙️ Prerequisites
- Development Setup
- Port-Forwarding for Local Access
- NGINX Backend Service Name: Docker Compose vs Kubernetes
- Cleaning Up Duplicate or Crashing Deployments and Pods in Kubernetes
- Debugging Frontend Pod Crashes: NGINX SSL Certificate Errors
- Connecting to the Database from Your Host (DBeaver, etc.)
- 🎯 Kubernetes Namespace Management
- 🆕 Namespaced Development Environment with Helm
- 🔒 Production Security & Best Practices
- 🆕 Recent Improvements & Troubleshooting
- 🚀 Production Deployment Pipeline (CI/CD)
- CI/CD Kubernetes Deployment Setup
- Using Private Docker Registry with Kubernetes
- Production Secrets Management (Gitea as Source of Truth)
- CI/CD Pipeline Behavior: Multiple Merges to Main
📁 Project Structure
fusero-app-boilerplate
├── chart/ # Helm chart for Kubernetes
│ ├── Chart.yaml
│ ├── values.dev.yaml
│ ├── values.prod.yaml
│ └── templates/
├── config/
├── coverage/
├── dist/
├── docs/
├── frontend/ # React frontend app
│ ├── public/
│ └── src/
├── mikro-orm.config.ts
├── nginx/
├── node_modules/
├── package.json
├── package-lock.json
├── docker-compose.yaml
├── docker-compose.dev.yaml
├── .gitignore
├── .gitea-ci.yaml
├── .prettierrc.json
├── .eslintrc.json
├── architecture.excalidraw
├── src/ # Node.js backend source
│ ├── apps/
│ ├── constants/
│ ├── database/
│ ├── middleware/
│ ├── plugins/
│ ├── shared/
│ ├── tests/
│ ├── types/
│ └── ...
├── test/
├── utils/
└── README.md
⚙️ Prerequisites
- Node.js (v20 or higher)
- npm (v9 or higher)
- Docker and Docker Compose
- Git
Development Setup
Important Note: Database Must Run in Docker
The PostgreSQL database must always run in Docker, regardless of your development setup choice. This ensures consistent database behavior across all environments.
To start the database:
docker build -t fusero-frontend-dev:local ./frontend
2. (If using a remote registry) Push the image
docker push <your-registry>/fusero-frontend-dev:local
3. Upgrade the Helm release
helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml
4. Restart the frontend deployment
kubectl rollout restart deployment/fusero-frontend -n fusero
Port-Forwarding for Local Access
To access your services running in Kubernetes from your local machine, use these commands:
Frontend (React app)
kubectl port-forward -n fusero-dev svc/fusero-frontend-service 3000:80
- Access at: http://localhost:3000
Backend (API)
kubectl port-forward -n fusero-dev svc/fusero-backend-service 14000:14000
- Access at: http://localhost:14000
Database
kubectl port-forward -n fusero-dev svc/postgres-service 5432:5432
- Access at: localhost:5432
NGINX Backend Service Name: Docker Compose vs Kubernetes
If your frontend uses NGINX to proxy API requests, you must update the backend service name depending on your environment:
- Docker Compose/local: The backend may be named
fusero-app-backend
. - Kubernetes: The backend service is named
fusero-backend-service
.
How to update the NGINX config for Kubernetes
Edit frontend/nginx.conf
:
Change this:
proxy_pass http://fusero-app-backend:14000/;
To this:
proxy_pass http://fusero-backend-service:14000/;
Then rebuild and redeploy the frontend:
docker build -t fusero-frontend-dev:local ./frontend
# (push if needed)
helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml
kubectl rollout restart deployment/fusero-frontend -n fusero
If you see an NGINX error like host not found in upstream
, this is the cause!
Cleaning Up Duplicate or Crashing Deployments and Pods in Kubernetes
If you see multiple frontend or backend pods (or CrashLoopBackOff errors), clean up your namespace with these steps:
1. List deployments and pods
kubectl get deployments -n fusero
kubectl get pods -n fusero
2. Delete old or crashing deployments (example IDs from your cluster)
kubectl delete deployment fusero-frontend-65cb8db99d -n fusero
kubectl delete deployment fusero-frontend-74fcbb778 -n fusero
3. Delete old or crashing pods (example IDs from your cluster)
kubectl delete pod fusero-frontend-65cb8db99d-f2lhr -n fusero
kubectl delete pod fusero-frontend-74fcbb778-v89gm -n fusero
Tip: Only keep the latest, healthy pods and deployments. If in doubt, check with kubectl get deployments -n fusero
and kubectl get pods -n fusero
before deleting.
Debugging Frontend Pod Crashes: NGINX SSL Certificate Errors
If your frontend pod crashes with an error like:
nginx: [emerg] cannot load certificate "/etc/nginx/certs/fusero-selfsigned.crt": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory)
This means NGINX is trying to load an SSL certificate that does not exist in the pod.
How to fix for Kubernetes (Recommended)
- Edit
frontend/nginx.conf
:- Change:
listen 14443 ssl; ssl_certificate /etc/nginx/certs/fusero-selfsigned.crt; ssl_certificate_key /etc/nginx/certs/fusero-selfsigned.key;
- To:
listen 8080; # (remove the ssl_certificate and ssl_certificate_key lines)
- Change:
- Rebuild the frontend Docker image:
docker build --no-cache -t fusero-frontend-dev:local ./frontend
- (If using a remote registry) Push the image.
- Redeploy with Helm:
helm upgrade fusero ./chart -n fusero -f chart/values.dev.yaml
- Check pod status:
kubectl get pods -n fusero
This will make NGINX listen on port 8080 without SSL, which is standard for in-cluster Kubernetes services.
Connecting to the Database from Your Host (DBeaver, etc.)
To connect to the Postgres database running in Kubernetes from your local machine (for example, using DBeaver or another SQL client):
-
Port-forward the Postgres service:
kubectl port-forward svc/postgres-service 5432:5432
- Keep this terminal open while you use your database client.
- If port 5432 is in use on your machine, you can use another local port (e.g.,
15432:5432
) and connect to port 15432 in your client.
-
Database connection settings:
- Host:
localhost
- Port:
5432
(or your chosen local port) - Database:
fusero-boilerplate-db
- Username:
root
- Password:
root123
- Host:
-
Open DBeaver (or your preferred client) and create a new Postgres connection using the above settings.
-
Test the connection.
🎯 Kubernetes Namespace Management
Development Namespace Setup
# Create development namespace
kubectl create namespace fusero-dev
# Set current context to development namespace
kubectl config set-context --current --namespace=fusero-dev
# Deploy to development namespace
helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml
Production Namespace Setup
# Create production namespace
kubectl create namespace fusero-prod
# Set current context to production namespace
kubectl config set-context --current --namespace=fusero-prod
# Deploy to production namespace
helm upgrade --install fusero ./chart -n fusero-prod -f chart/values.prod.yaml
Namespace Management Commands
# List all namespaces
kubectl get namespaces
# Switch between namespaces
kubectl config set-context --current --namespace=<namespace-name>
# View resources in current namespace
kubectl get all
# Delete namespace (be careful!)
kubectl delete namespace <namespace-name>
Recommended Kubernetes GUI Tools
-
Lens - The most popular Kubernetes IDE
- Download: https://k8slens.dev/
- Features:
- Real-time cluster monitoring
- Multi-cluster management
- Namespace isolation
- Resource visualization
- Log streaming
- Terminal access
-
K9s - Terminal-based UI
- Install:
brew install k9s
(Mac) orscoop install k9s
(Windows) - Features:
- Fast navigation
- Resource management
- Log viewing
- Port forwarding
- Install:
-
Octant - Web-based UI
- Install: https://octant.dev/
- Features:
- Resource visualization
- Configuration management
- Log viewing
- Port forwarding
🆕 Namespaced Development Environment with Helm
You have configured your Kubernetes development environment to use a dedicated namespace (fusero-dev
) for all core services (backend, frontend, and database). This ensures resource isolation and easier management between development and production.
What was changed:
- All Kubernetes service templates (
backend-service.yaml
,frontend-service.yaml
,postgres-service.yaml
) now use a namespace variable. - The development values file (
chart/values.dev.yaml
) setsglobal.namespace: fusero-dev
and updates all internal service hostnames to use this namespace. - Helm deployments now target the
fusero-dev
namespace for all dev resources.
How to use:
-
Create the development namespace (if not already created):
kubectl create namespace fusero-dev
-
Deploy all services to the namespace:
helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml
-
Check running pods and services:
kubectl get all -n fusero-dev
-
If you update environment variables or service hostnames, repeat the Helm upgrade command.
-
If you see errors about immutable fields (e.g., for Jobs), delete the old Job before redeploying:
kubectl delete job <job-name> -n fusero-dev helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml
Why use namespaces?
- Keeps dev and prod resources isolated
- Makes it easy to clean up or redeploy all dev resources
- Prevents accidental cross-environment access
🔒 Production Security & Best Practices
Environment Variables & Secrets
The application uses a secure secrets management approach:
-
Development Environment:
- Use
.env
files locally (gitignored) - Copy from
.env.example
as template
- Use
-
Production Environment:
- Secrets are managed through Gitea CI/CD secrets
- Template file:
chart/secrets.prod.template.yaml
- Actual secrets are generated during deployment
- Never commit actual secrets to the repository
-
Required Secrets:
- Database credentials
- Admin user credentials
- Security keys (encryption, JWT)
- API keys (ChatGPT, Canvas)
-
Secrets in CI/CD:
- Secrets are stored in Gitea CI/CD settings
- Automatically injected during deployment
- Used to generate
secrets.prod.yaml
at runtime
-
Security Best Practices:
- All secrets files are gitignored
- Template files contain placeholder values
- Production secrets are never stored in the repository
- Regular rotation of secrets recommended
HTTPS & Certificates
- In production, use trusted certificates (e.g., Let's Encrypt).
- Configure NGINX to enforce HTTPS.
CORS & Security Headers
- Lock down CORS settings in production.
- Use security headers (e.g., HSTS, CSP).
Logging & Monitoring
- Configure logging for production (e.g., ELK stack, Datadog).
- Set up basic monitoring (e.g., Prometheus, Grafana).
Database Backup
- Regularly backup your production database.
- Example: Use
pg_dump
or a managed backup service.
CI/CD & Automated Testing
- Run tests before deploying to production.
- Example CI/CD workflow:
# .github/workflows/deploy.yml name: Deploy to Production on: push: branch: main jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Run Tests run: npm test - name: Deploy to Kubernetes run: helm upgrade --install fusero ./chart -n fusero-prod -f chart/values.prod.yaml
Troubleshooting Production
- Common issues:
- Database connection errors: Check secrets and network policies.
- Pod crashes: Check logs with
kubectl logs <pod-name> -n fusero-prod
.
- Rollback: Use
helm rollback fusero <revision> -n fusero-prod
.
🆕 Recent Improvements & Troubleshooting
- Consistent File Extensions: All Kubernetes and Helm YAML files now use the
.yaml
extension for consistency. - Secrets Management:
- Development secrets are stored in
chart/secrets.dev.yaml
(gitignored). - Production secrets are generated by CI/CD as
chart/secrets.prod.yaml
from Gitea secrets. - All values files (
values.dev.yaml
,values.prod.yaml
) reference secrets via environment variables.
- Development secrets are stored in
- CORS Configuration:
- For local development, set
CORS_ORIGIN: "http://localhost:3000"
inchart/values.dev.yaml
to allow credentialed requests from the frontend. - Do not use
*
for CORS origin if you need credentials.
- For local development, set
- Kubernetes Job Immutability:
- If you update environment variables or secrets, you must delete the old migration/seed job before redeploying:
kubectl delete job fusero-backend-db-init -n fusero-dev helm upgrade --install fusero ./chart -n fusero-dev -f chart/values.dev.yaml -f chart/secrets.dev.yaml
- If you update environment variables or secrets, you must delete the old migration/seed job before redeploying:
- Port Forwarding for Local Access:
- Backend:
kubectl port-forward -n fusero-dev svc/fusero-backend-service 14000:14000
- Frontend:
kubectl port-forward -n fusero-dev svc/fusero-frontend-service 3000:80
- Database:
kubectl port-forward -n fusero-dev svc/postgres-service 5432:5432
- Backend:
- Testing Login with curl:
curl -i -X POST http://localhost:14000/api/v1/app/users/login \ -H "Content-Type: application/json" \ -H "Origin: http://localhost:3000" \ -d '{"username":"admin","password":"admin123"}'
- Troubleshooting 401 Errors:
- If login fails after a redeploy:
- Ensure secrets and values are in sync.
- Re-run the seed job as above.
- Check backend logs for authentication errors:
kubectl logs -n fusero-dev -l app=fusero-backend --tail=100
- If login fails after a redeploy:
🚀 Production Deployment Pipeline (CI/CD)
-
On every push/merge to
main
, the Gitea CI/CD pipeline will:- Build and push Docker images for backend and frontend.
- Generate
secrets.prod.yaml
from Gitea CI/CD secrets. - Delete the old migration/seed job (
fusero-backend-db-init
) to ensure a fresh run. - Deploy the app with Helm, which triggers the migration/seed job.
- Wait for the migration/seed job to complete.
- Fail the pipeline if the job fails (with logs for debugging).
- Verify the deployment.
-
This ensures your database is always migrated and seeded with every deploy, and you'll know immediately if something goes wrong.
-
To trigger a production deployment, just push or merge to
main
.
CI/CD Kubernetes Deployment Setup
To enable automated deployment to your Kubernetes cluster from CI/CD (Gitea Actions):
-
Get your kubeconfig file from your Kubernetes master node or provider.
- For self-hosted clusters, it's usually at
~/.kube/config
on the master node. - For managed clusters, download it from your provider's dashboard.
- For self-hosted clusters, it's usually at
-
Edit the kubeconfig file:
- Change the
server:
field to use your cluster's public IP or DNS, e.g.:
(For IPv6, use square brackets around the address.)server: https://[YOUR_PUBLIC_IP_OR_DNS]:6443
- Change the
-
Base64-encode the kubeconfig file as a single line:
- On Linux:
base64 -w 0 /path/to/your/kubeconfig
- On Mac:
base64 /path/to/your/kubeconfig | tr -d '\n'
- On Linux:
-
Add the base64 string as a secret in your Gitea repository:
- Go to Settings → Secrets
- Name:
KUBE_CONFIG
- Value: (paste the base64 string)
-
Make sure port 6443 is open to your CI/CD runner's IP in your VPS firewall/security group.
-
Your pipeline will now be able to deploy to your Kubernetes cluster.
Using Private Docker Registry with Kubernetes
If you use a private Docker registry (like registry.liquidrinu.com), you must create a Kubernetes secret and reference it in your deployments:
-
Create the registry secret:
kubectl create secret docker-registry regcred \ --docker-server=registry.liquidrinu.com \ --docker-username=YOUR_REGISTRY_USERNAME \ --docker-password=YOUR_REGISTRY_PASSWORD \ --docker-email=YOUR_EMAIL \ -n fusero-prod
-
Reference the secret in your deployment YAMLs: In your deployment spec, add:
imagePullSecrets: - name: regcred
Example:
spec: template: spec: imagePullSecrets: - name: regcred containers: - name: backend image: ...
This allows Kubernetes to authenticate to your private registry and pull images securely.
Production Secrets Management (Gitea as Source of Truth)
- In production, all sensitive values (like
POSTGRES_PASSWORD
,DEFAULT_ADMIN_PASSWORD
, etc.) are managed as secrets in your Gitea repository (Settings → Secrets). - The CI/CD pipeline uses these secrets to generate
chart/secrets.prod.yaml
and other files at runtime. - Helm uses these generated files to set environment variables for your Kubernetes resources.
- The Postgres password is set from the secret only when the database is first initialized (i.e., when the persistent volume is empty). Changing the secret later will not update the password for an existing database unless you reset the DB or delete the volume.
- Summary: Gitea secrets are the source of truth for production. Always update secrets in Gitea and redeploy to apply changes to new pods.
CI/CD Pipeline Behavior: Multiple Merges to Main
- If multiple merges or pushes happen to the
main
branch in quick succession, your CI/CD system will start a separate pipeline for each commit. - These pipelines will run in parallel unless your CI/CD is configured to queue or cancel previous runs.
- This can result in race conditions, where the last pipeline to finish will determine the final deployed state.
- Best practice: Avoid merging multiple large changes to
main
at the same time. Wait for the pipeline to finish before merging the next PR, or configure your CI/CD to cancel previous runs on new pushes.