Skip to main content

Deployment

UniPulse runs on a VPS (Ubuntu) using Docker Compose for the main application and PM2 for the Control Center.


Production Architecture​


Deployment Steps​

1. Build the Application​

# On the VPS (or in CI/CD pipeline)
cd Pulse
npm install
npm run build

2. Deploy with Docker Compose​

docker compose -f docker-compose.prod.yml up -d --build

This starts all production containers:

ContainerHealth CheckAuto-Restart
postgresTCP 5432always
redisTCP 6379always
apiGET /api/v1/healthalways
webHTTP 200always
certbot-On SSL renewal

3. Run Database Migrations​

docker compose -f docker-compose.prod.yml exec api npx prisma migrate deploy
Migration Order

Always run migrations after the containers are up but before sending traffic to the new version. Ensure the migration is backward-compatible with the previous API version during the transition.

4. Verify Deployment​

# Check API health
curl https://app.unipulse.tech/api/v1/health

# Check container status
docker compose -f docker-compose.prod.yml ps

# Check logs
docker compose -f docker-compose.prod.yml logs -f api

# Check Control Center
curl https://ops.unipulse.tech

Control Center Deployment​

The Control Center runs independently on PM2 (not Docker) so it survives container outages:

cd UniPulse-Control-Center

# First-time setup
./setup.sh

# Subsequent deployments
./deploy.sh

deploy.sh performs:​

StepCommand
Pull latest codegit pull origin main
Install dependenciesnpm install
Build frontend + backendnpm run build
Run migrationsnpx prisma migrate deploy
Restart PM2pm2 restart ecosystem.config.js

PM2 Configuration​

// ecosystem.config.js
module.exports = {
apps: [{
name: 'control-center',
script: './apps/server/dist/index.js',
max_memory_restart: '500M',
max_restarts: 10,
log_type: 'json',
env: {
NODE_ENV: 'production',
PORT: 3001,
},
}],
};
SettingValuePurpose
max_memory_restart500MAuto-restart if memory exceeds 500MB
max_restarts10Maximum restart attempts before stopping
log_typejsonStructured JSON logging

CI/CD Pipeline​

GitHub Actions handles automated checks and deployment:

CI Checks (on PR)​

CheckCommandBlocks Merge
Lintnpm run lintYes
Type checknpm run typecheckYes
Buildnpm run buildYes

CD (on merge to main)​

StepAction
SSH to VPSUsing GitHub Secrets for SSH key
Pull latestgit pull origin main
BuildDocker Compose build
Migrateprisma migrate deploy
Restartdocker compose restart
Health checkVerify /api/v1/health returns 200

Rollback Procedure​

Quick Rollback (Docker)​

# Stop current containers
docker compose -f docker-compose.prod.yml down

# Checkout previous version
git checkout <previous-tag>

# Rebuild and start
docker compose -f docker-compose.prod.yml up -d --build

# Re-apply migrations (if needed)
docker compose -f docker-compose.prod.yml exec api npx prisma migrate deploy

Control Center Rollback​

cd UniPulse-Control-Center
git checkout <previous-tag>
npm install && npm run build
pm2 restart ecosystem.config.js
Database Rollback

Prisma Migrate does not support automatic rollback. If a migration needs to be reversed, you must write a new migration that undoes the changes. Always test migrations on staging first and back up the production database before deploying.


Monitoring Post-Deployment​

CheckHowExpected
API healthcurl /api/v1/health200 OK
Container statusdocker compose psAll "running"
API logsdocker compose logs -f apiNo errors
Queue processingAdmin dashboard or Redis CLIJobs being processed
Control Centerhttps://ops.unipulse.techDashboard loads
SSL certificatesBrowser padlockValid certificate
Database connectivityPrisma Studio or API queriesQueries succeed

SSL Certificate Management​

# Initial certificate generation
docker compose -f docker-compose.prod.yml run --rm certbot certonly \
--webroot -w /var/www/certbot \
-d app.unipulse.tech

# Auto-renewal (add to crontab)
0 0 1 * * docker compose -f docker-compose.prod.yml run --rm certbot renew

Cross-Reference