Backend Node.js Helpers
CI/CD pipeline helpers for deploying Node.js API servers at commtool. This repository provides reusable GitLab CI templates, Dockerfiles, and docker-compose configurations for both development and production deployments.
Quick Start
Include this in your API server's .gitlab-ci.yml:
variables:
API_NAME: my-api
SECRET_PATH: backend/my-api
include:
- project: 'ci-helpers/backend-nodejs-helpers'
ref: main
file: 'pipelines.yaml'
Repository Structure
backend-nodejs-helpers/
├── pipelines.yaml # Main CI include (stages, jobs, templates)
├── pipeline-build-base.yaml # Base image build pipeline
├── pipeline-dev.ct.yaml # Development deployment pipeline
├── pipeline-prod.yaml # Production deployment pipeline
├── Dockerfile-dev # Development container image
├── Dockerfile-prod # Production container image
├── docker-compose-dev.yml # Dev deployment configuration
├── docker-compose-prod.yml # Prod deployment configuration
├── entrypoint.dev.sh # Development entrypoint script
├── entrypoint2.sh # Alternative entrypoint
├── scripts/ # Helper scripts
│ └── vscode-debug.sh # VS Code debugger setup
├── templates/ # Additional templates
└── documentation/ # Documentation files
├── SSH-SECURITY.md # SSH security practices
├── vscode-debug.md # VS Code debugging guide
└── extensions-readme.md # VS Code extensions
Pipeline Stages
| Stage | Description |
|---|---|
.pre |
Prepare CI helpers (clone this repo) |
base |
Build base Docker image (on Dockerfile changes) |
dev |
Deploy to development environment |
cleanup_dev |
Clean up old development deployments |
prod |
Deploy to production (main/master branch only) |
Configuration
Required Variables
Set these in your .gitlab-ci.yml:
variables:
API_NAME: my-api # Used in container/URL naming
SECRET_PATH: backend/my-api # Vault secret path prefix
Optional Variables
variables:
API_BASE: commtool.org # Base domain (default: commtool.org)
API_DEV_BASE: dev.commtool.org # Dev base domain
Vault Secrets
The pipeline expects secrets in Vault at:
gitlab-ci/data/${SECRET_PATH}/dev # Development secrets
gitlab-ci/data/${SECRET_PATH}/prod # Production secrets
SSH Configuration
SSH credentials for deployment are stored in Vault:
gitlab-ci/data/ssh-configs/${GITLAB_USER_LOGIN} # Dev (per-user)
Prod uses no SSH (image build/push only).
Development Deployment
Development deployments are per-user and per-branch:
- Path:
~/apis/${CI_PROJECT_NAME}/${CI_COMMIT_BRANCH} - URL:
https://${username}-p${project_id}-${branch}.dev.commtool.org - Debug Port: Auto-generated based on user/project hash
VS Code Debugging
The pipeline sets up VS Code remote debugging:
- A unique debug port is generated per user/project
- SSH tunnel configuration is provided
launch.jsonis created automatically
See documentation/vscode-debug.md for setup instructions.
Production Deployment
Production runs on main/master branch or tags. The prod job only builds and pushes the release image — there is no host deploy (no compose/env files are copied via SSH):
- Build production Docker image
- Push to container registry (
${CI_COMMIT_SHA},latest, and the release tag) - Deploy on the host via compose (bump the image tag, recreate the container)
- URL:
https://${API_NAME}.commtool.org
Extending docker-compose
To add custom services or volumes, create docker-compose-extra-dev.yml or docker-compose-extra-prod.yml in your project:
# docker-compose-extra-dev.yml
services:
${COMPOSE_PROJECT_NAME}:
volumes:
- ./custom-data:/app/data
environment:
- CUSTOM_VAR=value
The pipeline will automatically merge this with the base template.
Base Image
The base image is built when Dockerfile changes:
# Your project's Dockerfile
FROM node:22-slim
# ... your base setup
The base image is tagged as ${CI_REGISTRY_IMAGE}:base and used by dev/prod builds.
Features
Hot Reloading
Development containers use nodemon for automatic restart on code changes:
{
"watch": ["src"],
"ext": "js,json",
"exec": "node --inspect=0.0.0.0:9229 src/server.js"
}
Traefik Integration
Containers are automatically configured with Traefik labels for:
- HTTPS termination
- Automatic routing based on hostname
- Load balancing (production)
Health Checks
Production containers include health checks:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
Security
- No SSH keys in images - Keys are mounted at runtime or fetched from Vault
- JWT authentication - Short-lived tokens for Vault access
- Per-user isolation - Dev deployments use user-specific credentials
- Read-only mounts - SSH keys mounted as read-only
See documentation/SSH-SECURITY.md for security details.
Troubleshooting
Pipeline fails at SSH step
- Check Vault path exists for your user
- Verify SSH key is authorized on target server
- Check
/tmp/ssh.envwas created
Container won't start
- Check
docker compose logson target server - Verify environment variables in
envfile - Check Vault secrets path is correct
Base image not found
- Ensure
basestage ran successfully - Check container registry for
:basetag - Manually trigger base build if needed
Debug port not working
- Check SSH tunnel is established
- Verify debug port in
launch.jsonmatches generated port - Ensure container is running with
--inspectflag
Difference from backend-bot-helpers
| Feature | backend-nodejs-helpers | backend-bot-helpers |
|---|---|---|
| Use case | HTTP API servers | Event-driven bots |
| HTTP support | Always included | Auto-detected (optional) |
| Deployment path | ~/apis/ |
~/bots/ |
| docker-compose | Single template | HTTP + nohttp templates |
Related Repositories
- backend-bot-helpers - For bot applications
- frontend-vite-helpers - For Vite frontend apps
- docu-helpers - For documentation sites
Contributing
- Create a branch from
dev - Make changes and test with an API project
- Create merge request to
dev - After testing, merge to
mainfor production use