Skip to main content

Staging Deployment Guide

This document covers the full end-to-end staging deployment setup for Kloyst, including CI/CD pipelines, server layout, Nginx, monitoring, and the complete port allocation map.


Server Details​

PropertyValue
IP49.205.216.14
JenkinsSame server (local)
Image RegistryNone β€” images built on-server
DB StrategyIsolated staging DB (kloyst_staging)

Repository β†’ Deployment Mapping​

RepoBranchPipelineServer PathDomain
kloyst-corestagingkloyst-core/Jenkinsfile/srv/apps/kloyst-core/api.staging.kloyst.com
kloyst-frontstagingkloyst-front/Jenkinsfile/srv/apps/kloyst-front/staging.vendor.kloyst.com

Both pipelines are fully independent β€” deploying the backend does NOT redeploy the frontend, and vice versa.


CI/CD Pipeline Flow (Both Repos)​

Developer pushes to 'staging' branch
β”‚
Jenkins detects push (webhook or poll)
β”‚
β”Œβ”€β”€β”€ Pipeline Stages ────────────────────────────────────────┐
β”‚ 1. Checkout SCM β”‚
β”‚ 2. Inject secrets (Jenkins secret file β†’ .env on server) β”‚
β”‚ 3. rsync source code β†’ server (excludes: .git, node_mods) β”‚
β”‚ 4. docker build (on server, no external registry) β”‚
β”‚ 5. docker compose up -d --remove-orphans β”‚
β”‚ 6. Symlink Nginx config β†’ /etc/nginx/auto-sites/ β”‚
β”‚ 7. nginx -t && systemctl reload nginx β”‚
β”‚ 8. Health check (curl with 12 retries, 5s interval) β”‚
β”‚ 9. docker image prune -f + docker builder prune -f β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Server Directory Layout​

/srv/apps/
β”œβ”€β”€ kloyst-core/ ← backend app (rsync'd from Jenkins)
β”‚ β”œβ”€β”€ docker-compose.staging.yml
β”‚ β”œβ”€β”€ Dockerfile
β”‚ β”œβ”€β”€ nginx/
β”‚ β”‚ └── staging-api.conf ← symlinked to /etc/nginx/auto-sites/
β”‚ β”œβ”€β”€ monitoring/ ← monitoring stack
β”‚ β”‚ β”œβ”€β”€ docker-compose.monitoring.yml
β”‚ β”‚ β”œβ”€β”€ loki-config.yml
β”‚ β”‚ β”œβ”€β”€ promtail-config.yml
β”‚ β”‚ β”œβ”€β”€ grafana-provisioning/
β”‚ β”‚ β”‚ β”œβ”€β”€ datasources/loki.yml
β”‚ β”‚ β”‚ └── dashboards/dashboards.yml
β”‚ β”‚ β”œβ”€β”€ nginx/monitoring.conf ← symlinked to /etc/nginx/auto-sites/
β”‚ β”‚ β”œβ”€β”€ .env ← (manual β€” Grafana/Dozzle passwords)
β”‚ β”‚ └── README.md
β”‚ └── logs/ ← Winston log files (host volume)
β”‚ β”œβ”€β”€ system-YYYY-MM-DD.log
β”‚ β”œβ”€β”€ exception-YYYY-MM-DD.log
β”‚ β”œβ”€β”€ network-YYYY-MM-DD.log
β”‚ └── debug-YYYY-MM-DD.log ← dev only, empty in staging
β”‚
β”œβ”€β”€ kloyst-front/ ← frontend app (rsync'd from Jenkins)
β”‚ β”œβ”€β”€ docker-compose.staging.yml
β”‚ β”œβ”€β”€ Dockerfile
β”‚ └── nginx/
β”‚ └── staging.conf ← symlinked to /etc/nginx/auto-sites/
β”‚
└── fynli/ ← separate app (co-hosted)
└── ...

Docker Compose Services (Backend)​

File: kloyst-core/docker-compose.staging.yml

ContainerImageHost PortPurpose
kloyst-staging-api-gatewaykloyst-core:staging-latest127.0.0.1:3000REST API
kloyst-staging-webhook-workerkloyst-core:staging-latest127.0.0.1:3002Meta webhooks
kloyst-staging-worker-poolkloyst-core:staging-latestnoneBullMQ consumer
kloyst-staging-outbox-relaykloyst-core:staging-latestnoneDB→Redis relay
kloyst-staging-postgrespostgres:16-alpinenonePostgreSQL
kloyst-staging-redisredis:7.2-alpinenoneCache + BullMQ

All services share one internal Docker network kloyst-staging-net.
All host ports bound to 127.0.0.1 only (not reachable from the internet directly).


Docker Compose Services (Frontend)​

File: kloyst-front/docker-compose.staging.yml

ContainerImageHost PortPurpose
kloyst-front-stagingkloyst-front:staging-latest127.0.0.1:4001Next.js standalone server

Port is 4001 (not 4000) because Fynli's API claims port 4000 via network_mode: host.


Monitoring Stack​

File: kloyst-core/monitoring/docker-compose.monitoring.yml

Deployed separately (not part of the app pipeline):

cd /srv/apps/kloyst-core/monitoring
cp .env.example .env # set GRAFANA_PASSWORD and DOZZLE_PASSWORD
docker compose -f docker-compose.monitoring.yml up -d
ContainerImageHost PortURL
kloyst-monitoring-grafanagrafana/grafana:10.4.3127.0.0.1:3003logs.staging.kloyst.com
kloyst-monitoring-lokigrafana/loki:2.9.8127.0.0.1:3100internal
kloyst-monitoring-promtailgrafana/promtail:2.9.8noneinternal
kloyst-monitoring-dozzleamir20/dozzle:latest127.0.0.1:8888dozzle.staging.kloyst.com

Full Server Port Map​

PortAppServiceBinding
80, 443AllNginx0.0.0.0
3000KloystAPI Gateway127.0.0.1
3001FynliGrafana0.0.0.0
3002KloystWebhook Worker127.0.0.1
3003KloystGrafana (monitoring)127.0.0.1
3100KloystLoki127.0.0.1
4000Fynlifynli-api (host net)0.0.0.0
4001KloystFrontend (Next.js)127.0.0.1
5000MERN BlogStaging backend0.0.0.0
6379FynliRedis (host-level)127.0.0.1
8082MERN BlogStaging frontend0.0.0.0
8888KloystDozzle127.0.0.1
27017FynliMongoDB (host-level)127.0.0.1

Nginx Virtual Hosts​

DomainNginx configProxies to
staging.vendor.kloyst.comkloyst-front-staging.conf127.0.0.1:4001
api.staging.kloyst.comkloyst-api-staging.conf127.0.0.1:3000 + :3002
logs.staging.kloyst.comkloyst-monitoring.conf127.0.0.1:3003
dozzle.staging.kloyst.comkloyst-monitoring.conf127.0.0.1:8888
fynli.blogsage.infynli.conf:4000 + :3001

All staging domains protected by Nginx basic auth (username/password layer before app login).


Secrets Management​

All secrets managed in Jenkins Credentials (secret file type):

Jenkins Credential IDUsed byContent
KLOYST_CORE_STAGING_ENVBackend JenkinsfileFull .env for all 4 services
KLOYST_FRONT_STAGING_ENVFrontend JenkinsfileNEXT_PUBLIC_* + Meta vars
server-sshBoth JenkinsfilesSSH key for jenkins@49.205.216.14

First-Time Server Setup (Checklist)​

# 1. Create directory structure
sudo mkdir -p /srv/apps/{kloyst-core,kloyst-front}/nginx
sudo mkdir -p /srv/apps/kloyst-core/logs
sudo mkdir -p /srv/apps/kloyst-core/monitoring
sudo chown -R jenkins:jenkins /srv/apps/

# 2. Nginx auto-sites directory
sudo mkdir -p /etc/nginx/auto-sites
# Add to /etc/nginx/nginx.conf: include /etc/nginx/auto-sites/*.conf;

# 3. SSL certs (run once per domain group)
sudo certbot certonly --nginx \
-d staging.vendor.kloyst.com \
-d api.staging.kloyst.com

sudo certbot certonly --nginx \
-d logs.staging.kloyst.com \
-d dozzle.staging.kloyst.com

# 4. Basic auth file for staging
sudo htpasswd -c /etc/nginx/.htpasswd-staging <username>

# 5. Deploy monitoring stack
cd /srv/apps/kloyst-core/monitoring
cp .env.example .env && nano .env # set strong passwords
docker compose -f docker-compose.monitoring.yml up -d

# 6. Configure log rotation (host-level)
# See kloyst-core/logrotate/kloyst-staging