Skip to main content

Deploying Node.js Applications

This comprehensive guide covers everything you need to know about deploying Node.js applications via Deploy with Git, from simple Express servers to complex Next.js applications.

Overview​

Deploy with Git automatically detects Node.js applications by looking for:

  • package.json file
  • package-lock.json or yarn.lock or pnpm-lock.yaml
  • .nvmrc for version specification

Basic Node.js Deployment​

Simple Express Application​

docker run -d \
--name express-app \
-e GIT_REPO_URL=https://github.com/expressjs/express \
-e APP_PORT=3000 \
-p 3000:3000 \
runonflux/:latest

What Happens Automatically​

  1. Detection: Finds package.json and identifies as Node.js project
  2. Version Selection:
    • Checks .nvmrc file
    • Checks engines.node in package.json
    • Falls back to LTS version
  3. Package Manager Detection:
    • pnpm-lock.yaml → Uses pnpm
    • yarn.lock → Uses yarn
    • package-lock.json → Uses npm
  4. Installation: Runs appropriate install command
  5. Build: Checks for build script in package.json
  6. Start: Uses start script or main file

Framework-Specific Guides​

Next.js Applications​

Next.js requires building before running. Deploy with Git handles this automatically:

docker run -d \
--name nextjs-app \
-e GIT_REPO_URL=https://github.com/vercel/next.js/tree/canary/examples/blog \
-e APP_PORT=3000 \
-p 3000:3000 \
runonflux/orbit:latest

Optimization for Next.js:

docker run -d \
--name nextjs-app-optimized \
-e GIT_REPO_URL=https://github.com/your-username/nextjs-app \
-e APP_PORT=3000 \
-e NODE_VERSION=20.11.0 \
-e BUILD_COMMAND="npm run build" \
-e RUN_COMMAND="npm run start" \
-e NODE_ENV=production \
-p 3000:3000 \
runonflux/orbit:latest

Nuxt.js Applications​

docker run -d \
--name nuxt-app \
-e GIT_REPO_URL=https://github.com/your-username/nuxt-app \
-e APP_PORT=3000 \
-e BUILD_COMMAND="npm run build" \
-e RUN_COMMAND="npm run start" \
-p 3000:3000 \
runonflux/orbit:latest

Express/Fastify APIs​

docker run -d \
--name api-server \
-e GIT_REPO_URL=https://github.com/your-username/express-api \
-e APP_PORT=5000 \
-e NODE_ENV=production \
-e RUN_COMMAND="node server.js" \
-p 5000:5000 \
runonflux/orbit:latest

NestJS Applications​

docker run -d \
--name nestjs-app \
-e GIT_REPO_URL=https://github.com/your-username/nestjs-app \
-e APP_PORT=3000 \
-e BUILD_COMMAND="npm run build" \
-e RUN_COMMAND="npm run start:prod" \
-p 3000:3000 \
runonflux/orbit:latest

React/Vue/Angular SPAs​

For single-page applications that need to be built and served:

docker run -d \
--name react-app \
-e GIT_REPO_URL=https://github.com/your-username/react-app \
-e APP_PORT=3000 \
-e BUILD_COMMAND="npm run build" \
-e RUN_COMMAND="npx serve -s build -l 3000" \
-p 3000:3000 \
runonflux/orbit:latest

Package Manager Configuration​

NPM (Default)​

# Use npm ci for faster, reliable installs
-e INSTALL_COMMAND="npm ci"

# Production-only dependencies
-e INSTALL_COMMAND="npm ci --production"

# With legacy peer deps (for compatibility)
-e INSTALL_COMMAND="npm ci --legacy-peer-deps"

Yarn​

# Yarn with frozen lockfile
-e INSTALL_COMMAND="yarn install --frozen-lockfile"

# Yarn 2+ (Berry)
-e INSTALL_COMMAND="yarn install --immutable"

# Production only
-e INSTALL_COMMAND="yarn install --production --frozen-lockfile"

PNPM​

# PNPM with frozen lockfile
-e INSTALL_COMMAND="pnpm install --frozen-lockfile"

# Production only
-e INSTALL_COMMAND="pnpm install --prod --frozen-lockfile"

Node.js Version Management​

Specify Node Version​

# Using specific version
-e NODE_VERSION=20.11.0

# Using LTS codename
-e NODE_VERSION=lts/iron

# Using major version
-e NODE_VERSION=18

Version Detection Priority​

  1. NODE_VERSION environment variable
  2. .nvmrc file in repository
  3. engines.node in package.json
  4. Latest LTS version

Example .nvmrc​

20.11.0

Example package.json engines​

{
"engines": {
"node": ">=18.0.0 <21.0.0",
"npm": ">=9.0.0"
}
}

Environment Variables​

Common Node.js Variables​

docker run -d \
-e GIT_REPO_URL=https://github.com/your-username/node-app \
-e APP_PORT=3000 \
-e NODE_ENV=production \
-e NODE_OPTIONS="--max-old-space-size=2048" \
-e NPM_CONFIG_LOGLEVEL=warn \
-e DATABASE_URL=postgres://user:pass@host:5432/db \
-e REDIS_URL=redis://redis-host:6379 \
-e API_KEY=your_api_key_here \
-p 3000:3000 \
runonflux/orbit:latest

Using .env Files​

Create a .env file:

NODE_ENV=production
DATABASE_URL=postgres://localhost/mydb
REDIS_URL=redis://localhost:6379
API_KEY=secret_key_123
JWT_SECRET=jwt_secret_456

Your application will automatically load these with packages like dotenv.

Build Optimization​

Production Builds​

# Next.js production build
-e BUILD_COMMAND="npm run build"
-e NODE_ENV=production

# Custom webpack build
-e BUILD_COMMAND="webpack --mode production"

# TypeScript compilation
-e BUILD_COMMAND="tsc && npm run build"

Multi-stage Builds​

# Build and optimize in sequence
-e BUILD_COMMAND="npm run clean && npm run build && npm run optimize"

# With testing
-e BUILD_COMMAND="npm test && npm run build"

Build Caching​

Deploy with Git preserves node_modules between builds for faster deployments:

  • First deployment: Full installation
  • Subsequent updates: Only changed dependencies

Memory Management​

Configure Heap Size​

# Set max memory to 2GB
-e NODE_OPTIONS="--max-old-space-size=2048"

# Or use dedicated variable
-e MAX_OLD_SPACE_SIZE=2048

Memory-Intensive Applications​

docker run -d \
--name memory-app \
--memory="4g" \
--memory-swap="4g" \
-e GIT_REPO_URL=https://github.com/your-username/app \
-e APP_PORT=3000 \
-e MAX_OLD_SPACE_SIZE=3072 \
-p 3000:3000 \
runonflux/orbit:latest

Process Management​

Clustering with PM2​

If your app includes PM2:

-e RUN_COMMAND="pm2-runtime start ecosystem.config.js"

Example ecosystem.config.js:

module.exports = {
apps: [{
name: 'app',
script: './server.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production'
}
}]
};

Native Cluster Module​

-e WEB_CONCURRENCY=4  # Number of workers
-e RUN_COMMAND="node cluster.js"

Database Migrations​

Run Migrations on Deploy​

# Sequelize migrations
-e PRE_BUILD_COMMAND="npx sequelize-cli db:migrate"

# Prisma migrations
-e PRE_BUILD_COMMAND="npx prisma migrate deploy"

# TypeORM migrations
-e PRE_BUILD_COMMAND="npm run typeorm migration:run"

# Knex migrations
-e PRE_BUILD_COMMAND="npx knex migrate:latest"

Safe Migration Pattern​

docker run -d \
--name app-with-migrations \
-e GIT_REPO_URL=https://github.com/your-username/app \
-e APP_PORT=3000 \
-e DATABASE_URL=postgres://user:pass@db:5432/myapp \
-e PRE_BUILD_COMMAND="npm run db:backup && npm run db:migrate" \
-e POST_BUILD_COMMAND="npm run db:seed" \
-p 3000:3000 \
runonflux/orbit:latest

Private NPM Packages​

Using NPM Token​

# Set NPM token for private packages
-e NPM_TOKEN=your_npm_token_here

# Or configure registry
-e NPM_CONFIG_REGISTRY=https://npm.company.com/
-e NPM_CONFIG_ALWAYS_AUTH=true

.npmrc Configuration​

Create .npmrc in your repository:

//registry.npmjs.org/:_authToken=${NPM_TOKEN}
@company:registry=https://npm.company.com/
always-auth=true

Debugging Deployments​

Enable Debug Logging​

docker run -d \
--name debug-app \
-e GIT_REPO_URL=https://github.com/your-username/app \
-e APP_PORT=3000 \
-e LOG_LEVEL=debug \
-e DEBUG=* \
-e NODE_ENV=development \
-p 3000:3000 \
runonflux/orbit:latest

Access Container for Debugging​

# Execute commands in running container
docker exec -it debug-app /bin/bash

# Check Node version
docker exec debug-app node --version

# View installed packages
docker exec debug-app npm list

# Check running processes
docker exec debug-app ps aux

Common Issues and Solutions​

1. Module Not Found​

Problem: Error: Cannot find module 'express'

Solution:

-e INSTALL_COMMAND="npm ci"  # Use ci for clean install

2. Build Failures​

Problem: Build script fails

Solution:

# Skip build if not needed
-e BUILD_COMMAND="echo 'No build required'"

# Or use custom build
-e BUILD_COMMAND="npm run build:docker"

3. Port Already in Use​

Problem: Error: listen EADDRINUSE: address already in use

Solution:

# Ensure APP_PORT matches your app's actual port
-e APP_PORT=3001 # If your app uses 3001

4. Memory Errors​

Problem: FATAL ERROR: Reached heap limit

Solution:

-e NODE_OPTIONS="--max-old-space-size=4096"

Performance Optimization​

Production Best Practices​

docker run -d \
--name production-node-app \
-e GIT_REPO_URL=https://github.com/your-username/app \
-e APP_PORT=3000 \
-e NODE_ENV=production \
-e INSTALL_COMMAND="npm ci --production" \
-e BUILD_COMMAND="npm run build:prod" \
-e RUN_COMMAND="node dist/server.js" \
-e NODE_OPTIONS="--max-old-space-size=2048" \
-e WEB_CONCURRENCY=4 \
--memory="4g" \
--cpus="2" \
-p 3000:3000 \
runonflux/orbit:latest

Caching Strategies​

  1. Dependency Caching: Node modules are preserved between deployments
  2. Build Artifacts: Build outputs are retained unless code changes
  3. Static Assets: Consider CDN for static files

Monitoring​

Health Checks​

# Configure health endpoint
-e HEALTH_CHECK_PATH=/api/health
-e HEALTH_CHECK_INTERVAL=30

Implement in your app:

app.get('/api/health', (req, res) => {
res.status(200).json({ status: 'healthy' });
});

Logs​

# View application logs
docker logs -f container-name

# View specific log file
docker exec container-name tail -f /app/logs/app.log

# Check error logs
docker exec container-name tail -f /app/logs/error.log

Example Deployments​

Complete Next.js E-commerce​

docker run -d \
--name ecommerce \
-e GIT_REPO_URL=https://github.com/vercel/commerce \
-e APP_PORT=3000 \
-e NODE_VERSION=20.11.0 \
-e DATABASE_URL=postgres://user:pass@db:5432/commerce \
-e STRIPE_SECRET_KEY=sk_test_... \
-e NEXT_PUBLIC_STRIPE_KEY=pk_test_... \
-e PRE_BUILD_COMMAND="npx prisma migrate deploy" \
-e BUILD_COMMAND="npm run build" \
-e RUN_COMMAND="npm run start" \
-p 3000:3000 \
runonflux/orbit:latest

Microservice API​

docker run -d \
--name user-service \
-e GIT_REPO_URL=https://github.com/company/user-service \
-e APP_PORT=4000 \
-e NODE_ENV=production \
-e SERVICE_NAME=user-service \
-e KAFKA_BROKERS=kafka1:9092,kafka2:9092 \
-e REDIS_URL=redis://redis:6379 \
-e RUN_COMMAND="node --enable-source-maps dist/main.js" \
-p 4000:4000 \
runonflux/orbit:latest

Next Steps​