Home

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:

  1. A unique debug port is generated per user/project
  2. SSH tunnel configuration is provided
  3. launch.json is 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):

  1. Build production Docker image
  2. Push to container registry (${CI_COMMIT_SHA}, latest, and the release tag)
  3. 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

  1. Check Vault path exists for your user
  2. Verify SSH key is authorized on target server
  3. Check /tmp/ssh.env was created

Container won't start

  1. Check docker compose logs on target server
  2. Verify environment variables in env file
  3. Check Vault secrets path is correct

Base image not found

  1. Ensure base stage ran successfully
  2. Check container registry for :base tag
  3. Manually trigger base build if needed

Debug port not working

  1. Check SSH tunnel is established
  2. Verify debug port in launch.json matches generated port
  3. Ensure container is running with --inspect flag

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

Contributing

  1. Create a branch from dev
  2. Make changes and test with an API project
  3. Create merge request to dev
  4. After testing, merge to main for production use

source/src/Router/extraApi/extraController.js