Deployment Guide
Best practices for deploying and managing applications in production
Production Deployment Checklist
Essential steps before going live
Production Deployment Checklist
Ensure your SimplyStream application is production-ready with this comprehensive checklist.
Pre-Deployment
1. Testing
- All features tested in staging environment
- Load testing completed with expected user count
- Streaming quality verified across different network conditions
- Cross-browser compatibility tested (Chrome, Firefox, Safari, Edge)
- Mobile responsiveness verified
- Error handling tested for common failure scenarios
- Backup and recovery procedures tested
2. Performance Optimization
- Build size optimized (compression, code splitting)
- Images and assets optimized
- Streaming bitrate configured appropriately
- CDN configured for static assets
- Database queries optimized
- Caching strategy implemented
- API rate limits configured
3. Security
- HTTPS/SSL certificates configured
- API keys and secrets stored securely
- Authentication and authorization implemented
- Input validation and sanitization in place
- Content Security Policy (CSP) configured
- CORS properly configured
- DDoS protection enabled
- Regular security updates scheduled
4. Monitoring & Logging
- Error tracking configured (Sentry, LogRocket, etc.)
- Performance monitoring enabled (New Relic, DataDog, etc.)
- Uptime monitoring configured
- Log aggregation setup
- Alerts configured for critical issues
- Analytics tracking implemented
- User session recording enabled (optional)
5. Infrastructure
- Auto-scaling configured
- Load balancer setup
- Database backups automated
- Disaster recovery plan documented
- Multiple regions configured for redundancy
- CDN configured for global delivery
- DNS configured correctly
Configuration
Environment Variables
Ensure all environment variables are set:
# Production .env
NODE_ENV=production
SIMPLYSTREAM_API_KEY=your_production_api_key
SIMPLYSTREAM_PROJECT_ID=your_project_id
# Database
DATABASE_URL=your_production_database_url
REDIS_URL=your_redis_url
# Security
SESSION_SECRET=your_session_secret
JWT_SECRET=your_jwt_secret
# Monitoring
SENTRY_DSN=your_sentry_dsn
ANALYTICS_ID=your_analytics_id
# Email
SMTP_HOST=your_smtp_host
SMTP_PORT=587
SMTP_USER=your_smtp_user
SMTP_PASSWORD=your_smtp_password
SimplyStream Configuration
Configure production-specific settings:
import { SimplyStreamClient } from '@simplystream/sdk';
const client = new SimplyStreamClient({
apiKey: process.env.SIMPLYSTREAM_API_KEY,
projectId: process.env.SIMPLYSTREAM_PROJECT_ID,
environment: 'production',
timeout: 30000,
retryConfig: {
maxRetries: 3,
retryDelay: 1000
}
});
Deployment Steps
1. Build
# Install dependencies
npm ci --production
# Run production build
npm run build
# Verify build output
ls -la dist/
2. Database Migration
# Run database migrations
npm run migrate:production
# Verify migrations
npm run migrate:status
3. Upload Assets
# Upload build to SimplyStream
npm run upload:build
# Upload static assets to CDN
npm run upload:assets
4. Deploy Application
# Deploy to production
npm run deploy:production
# Verify deployment
npm run health:check
5. Smoke Testing
After deployment, verify critical functionality:
# Run smoke tests
npm run test:smoke
# Check endpoints
curl https://your-app.com/health
curl https://your-app.com/api/status
Post-Deployment
1. Monitoring
Monitor the application for the first hour:
- Check error rates in monitoring dashboard
- Verify all services are healthy
- Monitor response times and latency
- Check user sessions are being created
- Verify streaming sessions work correctly
2. Rollback Plan
Be prepared to rollback if issues occur:
# Rollback to previous version
npm run deploy:rollback
# Or manually specify version
npm run deploy:rollback --version=1.2.3
3. Communication
- Notify team of successful deployment
- Update status page
- Announce new features to users (if applicable)
- Update documentation
Performance Benchmarks
Track these metrics post-deployment:
| Metric | Target | Critical Threshold |
|---|---|---|
| Page Load Time | < 2s | < 5s |
| API Response Time | < 200ms | < 1s |
| Session Creation Time | < 3s | < 10s |
| Stream Start Time | < 5s | < 15s |
| Error Rate | < 0.1% | < 1% |
| Uptime | > 99.9% | > 99% |
Security Checklist
Authentication
- Secure session management implemented
- Password policies enforced
- Multi-factor authentication available
- Account lockout after failed attempts
- Session timeout configured
Data Protection
- Sensitive data encrypted at rest
- Sensitive data encrypted in transit
- PII handling compliant with regulations
- Data retention policies implemented
- Right to deletion implemented (GDPR)
API Security
- Rate limiting enabled
- API authentication required
- Input validation on all endpoints
- SQL injection protection
- XSS protection
- CSRF protection
Compliance
GDPR (if applicable)
- Privacy policy published
- Cookie consent implemented
- Data export functionality
- Data deletion functionality
- Data processing agreement signed
Accessibility
- WCAG 2.1 Level AA compliance
- Keyboard navigation supported
- Screen reader compatible
- Color contrast ratios met
- Alt text for all images
Documentation
- API documentation updated
- User documentation current
- Deployment procedures documented
- Runbook for common issues
- Architecture diagrams updated
Maintenance
Regular Tasks
Daily:
- Monitor error rates
- Check system health
- Review user feedback
Weekly:
- Review performance metrics
- Check backup integrity
- Review security logs
- Update dependencies
Monthly:
- Security audit
- Performance review
- Capacity planning
- Cost optimization review
Emergency Contacts
Maintain a list of emergency contacts:
Team Lead: [email protected], +1-xxx-xxx-xxxx
DevOps: [email protected], +1-xxx-xxx-xxxx
SimplyStream Support: [email protected]
Hosting Provider: [email protected]
Cost Optimization
- Auto-scaling configured correctly
- Unused resources identified and removed
- Reserved instances purchased (if applicable)
- CDN caching optimized
- Database query performance optimized
- Compression enabled
- Resource usage alerts configured
Final Verification
Before marking deployment as complete:
- Functionality: All features working as expected
- Performance: Meeting performance benchmarks
- Security: All security measures in place
- Monitoring: Alerts and monitoring active
- Documentation: All documentation updated
- Team: Team notified and aware of changes
- Rollback: Rollback plan tested and ready
Next Steps
- Set up CI/CD Integration for automated deployments
- Configure Docker & Containers for consistent environments
- Optimize Environment Configuration for different stages
CI/CD Integration
Automate deployments with CI/CD pipelines
CI/CD Integration
Automate your SimplyStream deployments with continuous integration and continuous deployment pipelines.
GitHub Actions
Basic Deployment Workflow
Create .github/workflows/deploy.yml:
name: Deploy to SimplyStream
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
SIMPLYSTREAM_API_KEY: ${{ secrets.SIMPLYSTREAM_API_KEY }}
SIMPLYSTREAM_PROJECT_ID: ${{ secrets.SIMPLYSTREAM_PROJECT_ID }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run linter
run: npm run lint
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v3
with:
name: build
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Download build artifact
uses: actions/download-artifact@v3
with:
name: build
path: dist/
- name: Deploy to SimplyStream
run: |
npm install -g @simplystream/cli
simplystream deploy \
--project-id $SIMPLYSTREAM_PROJECT_ID \
--api-key $SIMPLYSTREAM_API_KEY \
--build-path dist/
Advanced Workflow with Staging
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm test
- run: npm run test:e2e
deploy-staging:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
environment:
name: staging
url: https://staging.yourapp.com
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm run build
- name: Deploy to Staging
env:
SIMPLYSTREAM_API_KEY: ${{ secrets.STAGING_API_KEY }}
run: |
npm install -g @simplystream/cli
simplystream deploy \
--project-id ${{ secrets.STAGING_PROJECT_ID }} \
--environment staging
deploy-production:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://yourapp.com
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm run build:production
- name: Deploy to Production
env:
SIMPLYSTREAM_API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
run: |
npm install -g @simplystream/cli
simplystream deploy \
--project-id ${{ secrets.PRODUCTION_PROJECT_ID }} \
--environment production \
--release-notes "${{ github.event.head_commit.message }}"
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ github.run_number }}
release_name: Release ${{ github.run_number }}
body: ${{ github.event.head_commit.message }}
GitLab CI
.gitlab-ci.yml Configuration
stages:
- test
- build
- deploy
variables:
NODE_VERSION: '18'
test:
stage: test
image: node:${NODE_VERSION}
cache:
paths:
- node_modules/
script:
- npm ci
- npm run test
- npm run lint
artifacts:
reports:
junit: junit.xml
coverage: coverage/
build:
stage: build
image: node:${NODE_VERSION}
cache:
paths:
- node_modules/
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
deploy:staging:
stage: deploy
image: node:${NODE_VERSION}
environment:
name: staging
url: https://staging.yourapp.com
only:
- develop
script:
- npm install -g @simplystream/cli
- simplystream deploy
--project-id $STAGING_PROJECT_ID
--api-key $STAGING_API_KEY
--build-path dist/
--environment staging
deploy:production:
stage: deploy
image: node:${NODE_VERSION}
environment:
name: production
url: https://yourapp.com
only:
- main
when: manual
script:
- npm install -g @simplystream/cli
- simplystream deploy
--project-id $PRODUCTION_PROJECT_ID
--api-key $PRODUCTION_API_KEY
--build-path dist/
--environment production
CircleCI
.circleci/config.yml
version: 2.1
orbs:
node: circleci/[email protected]
jobs:
test:
docker:
- image: cimg/node:18.0
steps:
- checkout
- node/install-packages:
pkg-manager: npm
- run:
name: Run tests
command: npm test
- run:
name: Run linter
command: npm run lint
build:
docker:
- image: cimg/node:18.0
steps:
- checkout
- node/install-packages:
pkg-manager: npm
- run:
name: Build application
command: npm run build
- persist_to_workspace:
root: .
paths:
- dist
deploy:
docker:
- image: cimg/node:18.0
steps:
- checkout
- attach_workspace:
at: .
- run:
name: Install SimplyStream CLI
command: npm install -g @simplystream/cli
- run:
name: Deploy to SimplyStream
command: |
simplystream deploy \
--project-id $SIMPLYSTREAM_PROJECT_ID \
--api-key $SIMPLYSTREAM_API_KEY \
--build-path dist/
workflows:
test-build-deploy:
jobs:
- test
- build:
requires:
- test
- deploy:
requires:
- build
filters:
branches:
only: main
Jenkins
Jenkinsfile
pipeline {
agent any
environment {
SIMPLYSTREAM_API_KEY = credentials('simplystream-api-key')
SIMPLYSTREAM_PROJECT_ID = credentials('simplystream-project-id')
}
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Test') {
steps {
sh 'npm test'
sh 'npm run lint'
}
}
stage('Build') {
steps {
sh 'npm run build'
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh 'npm install -g @simplystream/cli'
sh '''
simplystream deploy \
--project-id $SIMPLYSTREAM_PROJECT_ID \
--api-key $SIMPLYSTREAM_API_KEY \
--build-path dist/
'''
}
}
}
post {
success {
slackSend color: 'good', message: "Deployment successful: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
}
failure {
slackSend color: 'danger', message: "Deployment failed: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
}
}
}
SimplyStream CLI
Installation
npm install -g @simplystream/cli
# or
yarn global add @simplystream/cli
Authentication
# Login interactively
simplystream login
# Or use API key
simplystream auth:set-key YOUR_API_KEY
Deployment Commands
# Deploy current directory
simplystream deploy
# Deploy specific build
simplystream deploy --build-path ./dist
# Deploy with custom configuration
simplystream deploy \
--project-id proj_123 \
--environment production \
--region us-west-2 \
--release-notes "Bug fixes and improvements"
# Deploy with version tag
simplystream deploy --tag v1.2.3
# List deployments
simplystream deployments list
# Rollback to previous version
simplystream deployments rollback
# Get deployment status
simplystream deployments status deployment_123
Automated Testing in CI/CD
Integration Tests
// tests/integration/deployment.test.ts
import { SimplyStreamClient } from '@simplystream/sdk';
describe('Deployment Tests', () => {
let client: SimplyStreamClient;
let sessionId: string;
beforeAll(() => {
client = new SimplyStreamClient({
apiKey: process.env.SIMPLYSTREAM_API_KEY,
projectId: process.env.SIMPLYSTREAM_PROJECT_ID
});
});
test('should create session successfully', async () => {
const session = await client.sessions.create({
buildId: process.env.BUILD_ID
});
expect(session.id).toBeDefined();
expect(session.status).toBe('creating');
sessionId = session.id;
});
test('session should become ready', async () => {
const session = await client.sessions.waitForReady(sessionId, {
timeout: 60000
});
expect(session.status).toBe('ready');
expect(session.url).toBeDefined();
}, 60000);
test('should load streaming page', async () => {
const response = await fetch(session.url);
expect(response.status).toBe(200);
});
afterAll(async () => {
if (sessionId) {
await client.sessions.terminate(sessionId);
}
});
});
Smoke Tests
#!/bin/bash
# smoke-test.sh
set -e
echo "Running smoke tests..."
# Test API health
echo "Testing API..."
curl -f https://api.yourapp.com/health || exit 1
# Test session creation
echo "Testing session creation..."
SESSION_ID=$(curl -X POST https://api.yourapp.com/sessions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"buildId":"'$BUILD_ID'"}' | jq -r '.id')
if [ -z "$SESSION_ID" ]; then
echo "Failed to create session"
exit 1
fi
echo "Smoke tests passed!"
Deployment Notifications
Slack Integration
# GitHub Actions
- name: Notify Slack
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: |
Deployment ${{ job.status }}
Branch: ${{ github.ref }}
Commit: ${{ github.sha }}
Author: ${{ github.actor }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
Email Notifications
// notify.ts
import { sendEmail } from './email-service';
async function notifyDeployment(result: DeploymentResult) {
await sendEmail({
to: '[email protected]',
subject: `Deployment ${result.status}: ${result.version}`,
html: `
<h2>Deployment ${result.status}</h2>
<ul>
<li>Version: ${result.version}</li>
<li>Environment: ${result.environment}</li>
<li>Duration: ${result.duration}s</li>
<li>Deployed by: ${result.author}</li>
</ul>
`
});
}
Best Practices
Use environment-specific configurations
- Separate configs for dev, staging, production
- Never commit secrets to version control
Implement proper testing
- Unit tests run on every commit
- Integration tests before deployment
- Smoke tests after deployment
Use semantic versioning
- Tag releases with version numbers
- Maintain changelog
Enable rollback capabilities
- Keep previous versions available
- Test rollback procedures
Monitor deployments
- Track deployment success/failure rates
- Monitor application health post-deployment
- Set up alerts for failures
Use deployment gates
- Manual approval for production
- Automated checks before deployment
- Gradual rollouts when possible
Next Steps
- Configure Docker & Containers for consistent deployments
- Set up Environment Configuration management
- Review Production Deployment Checklist
Docker & Containers
Deploy with Docker and Kubernetes
Docker & Containers
Deploy SimplyStream applications using Docker and container orchestration platforms.
Dockerfile
Basic Dockerfile
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Build application
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
# Copy built assets from builder
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
# Set environment
ENV NODE_ENV=production
# Expose port
EXPOSE 3000
# Start application
CMD ["node", "dist/server.js"]
Multi-Stage Dockerfile with SimplyStream
FROM node:18-alpine AS builder
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci
# Build application
COPY . .
RUN npm run build
# Upload to SimplyStream
FROM node:18-alpine AS uploader
WORKDIR /app
COPY --from=builder /app/dist ./dist
# Install SimplyStream CLI
RUN npm install -g @simplystream/cli
# Upload build (runs during image build)
ARG SIMPLYSTREAM_API_KEY
ARG SIMPLYSTREAM_PROJECT_ID
RUN simplystream deploy \
--build-path ./dist \
--project-id $SIMPLYSTREAM_PROJECT_ID \
--api-key $SIMPLYSTREAM_API_KEY
# Final runtime stage
FROM node:18-alpine
WORKDIR /app
# Copy application
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package.json ./
ENV NODE_ENV=production
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node healthcheck.js
CMD ["node", "dist/server.js"]
Docker Compose
Development Setup
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- '3000:3000'
environment:
- NODE_ENV=development
- SIMPLYSTREAM_API_KEY=${SIMPLYSTREAM_API_KEY}
- SIMPLYSTREAM_PROJECT_ID=${SIMPLYSTREAM_PROJECT_ID}
volumes:
- .:/app
- /app/node_modules
depends_on:
- redis
- postgres
redis:
image: redis:7-alpine
ports:
- '6379:6379'
volumes:
- redis-data:/data
postgres:
image: postgres:15-alpine
ports:
- '5432:5432'
environment:
- POSTGRES_DB=simplystream
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
redis-data:
postgres-data:
Production Setup
# docker-compose.prod.yml
version: '3.8'
services:
app:
image: your-registry/simplystream-app:latest
restart: unless-stopped
ports:
- '80:3000'
environment:
- NODE_ENV=production
- SIMPLYSTREAM_API_KEY=${SIMPLYSTREAM_API_KEY}
- SIMPLYSTREAM_PROJECT_ID=${SIMPLYSTREAM_PROJECT_ID}
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
max_attempts: 3
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- '443:443'
- '80:80'
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- app
Kubernetes
Deployment
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: simplystream-app
labels:
app: simplystream
spec:
replicas: 3
selector:
matchLabels:
app: simplystream
template:
metadata:
labels:
app: simplystream
spec:
containers:
- name: app
image: your-registry/simplystream-app:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: 'production'
- name: SIMPLYSTREAM_API_KEY
valueFrom:
secretKeyRef:
name: simplystream-secrets
key: api-key
- name: SIMPLYSTREAM_PROJECT_ID
valueFrom:
configMapKeyRef:
name: simplystream-config
key: project-id
resources:
requests:
memory: '256Mi'
cpu: '250m'
limits:
memory: '512Mi'
cpu: '500m'
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
Service
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: simplystream-service
spec:
selector:
app: simplystream
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
ConfigMap
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: simplystream-config
data:
project-id: 'proj_your_project_id'
environment: 'production'
region: 'us-west-2'
Secret
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: simplystream-secrets
type: Opaque
stringData:
api-key: 'your_api_key_here'
database-url: 'postgresql://user:pass@host:5432/db'
Ingress
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simplystream-ingress
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- yourapp.com
secretName: simplystream-tls
rules:
- host: yourapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: simplystream-service
port:
number: 80
Helm Chart
Chart.yaml
apiVersion: v2
name: simplystream
description: A Helm chart for SimplyStream application
type: application
version: 1.0.0
appVersion: '1.0.0'
values.yaml
replicaCount: 3
image:
repository: your-registry/simplystream-app
pullPolicy: IfNotPresent
tag: 'latest'
service:
type: LoadBalancer
port: 80
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: yourapp.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: simplystream-tls
hosts:
- yourapp.com
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 80
env:
- name: NODE_ENV
value: production
- name: SIMPLYSTREAM_PROJECT_ID
value: proj_your_project_id
secrets:
- name: SIMPLYSTREAM_API_KEY
value: your_api_key
Container Registry
Build and Push
# Build image
docker build -t your-registry/simplystream-app:latest .
# Tag with version
docker tag your-registry/simplystream-app:latest \
your-registry/simplystream-app:1.0.0
# Push to registry
docker push your-registry/simplystream-app:latest
docker push your-registry/simplystream-app:1.0.0
GitHub Container Registry
# .github/workflows/docker.yml
name: Docker Build and Push
on:
push:
branches: [main]
tags: ['v*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
AWS ECS
Task Definition
{
"family": "simplystream-app",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"containerDefinitions": [
{
"name": "app",
"image": "your-registry/simplystream-app:latest",
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"environment": [
{
"name": "NODE_ENV",
"value": "production"
},
{
"name": "SIMPLYSTREAM_PROJECT_ID",
"value": "proj_your_project_id"
}
],
"secrets": [
{
"name": "SIMPLYSTREAM_API_KEY",
"valueFrom": "arn:aws:secretsmanager:region:account:secret:simplystream-api-key"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/simplystream-app",
"awslogs-region": "us-west-2",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
Google Cloud Run
Deploy to Cloud Run
# Build container
gcloud builds submit --tag gcr.io/your-project/simplystream-app
# Deploy to Cloud Run
gcloud run deploy simplystream-app \
--image gcr.io/your-project/simplystream-app \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars NODE_ENV=production \
--set-secrets SIMPLYSTREAM_API_KEY=simplystream-api-key:latest \
--min-instances 1 \
--max-instances 10
Best Practices
1. Use Multi-Stage Builds
Reduce image size and improve security:
# Build stage
FROM node:18-alpine AS builder
# ... build steps
# Production stage
FROM node:18-alpine
COPY --from=builder /app/dist ./dist
# Only copy what's needed
2. Use .dockerignore
node_modules
npm-debug.log
.git
.env
.env.local
dist
coverage
.vscode
.idea
3. Run as Non-Root User
# Create app user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# Change ownership
RUN chown -R nodejs:nodejs /app
# Switch to app user
USER nodejs
4. Health Checks
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
5. Use Build Arguments
ARG NODE_VERSION=18
FROM node:${NODE_VERSION}-alpine
ARG BUILD_DATE
ARG VERSION
LABEL build_date=$BUILD_DATE
LABEL version=$VERSION
Monitoring
Prometheus Metrics
// metrics.ts
import promClient from 'prom-client';
const register = new promClient.Registry();
const httpRequestDuration = new promClient.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
registers: [register]
});
export { register, httpRequestDuration };
Expose Metrics Endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});
Next Steps
- Configure Environment Variables for different deployments
- Review Production Deployment Checklist
- Set up CI/CD Integration for automated builds
Environment Configuration
Manage environment variables and secrets
Environment Configuration
Manage environment variables and configuration across different deployment stages.
Environment Variables
Required Variables
# SimplyStream Configuration
SIMPLYSTREAM_API_KEY=your_api_key
SIMPLYSTREAM_PROJECT_ID=your_project_id
SIMPLYSTREAM_ENVIRONMENT=production # development, staging, production
# Application
NODE_ENV=production
PORT=3000
HOST=0.0.0.0
# Database
DATABASE_URL=postgresql://user:password@host:5432/database
DATABASE_POOL_SIZE=20
DATABASE_SSL=true
# Redis (for session/caching)
REDIS_URL=redis://host:6379
REDIS_PASSWORD=your_redis_password
# Security
SESSION_SECRET=your_session_secret
JWT_SECRET=your_jwt_secret
ENCRYPTION_KEY=your_encryption_key
# API Configuration
API_RATE_LIMIT=100
API_TIMEOUT=30000
Optional Variables
# Monitoring & Logging
SENTRY_DSN=https://[email protected]/project
LOG_LEVEL=info # debug, info, warn, error
NEW_RELIC_LICENSE_KEY=your_new_relic_key
# Email
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your_smtp_user
SMTP_PASSWORD=your_smtp_password
[email protected]
# Storage
AWS_ACCESS_KEY_ID=your_aws_key
AWS_SECRET_ACCESS_KEY=your_aws_secret
AWS_REGION=us-west-2
S3_BUCKET=your-bucket-name
# Analytics
GOOGLE_ANALYTICS_ID=UA-XXXXX-Y
MIXPANEL_TOKEN=your_mixpanel_token
Environment Files
Development (.env.development)
NODE_ENV=development
SIMPLYSTREAM_ENVIRONMENT=development
# Use local services
DATABASE_URL=postgresql://localhost:5432/simplystream_dev
REDIS_URL=redis://localhost:6379
# Relaxed security for development
SESSION_SECRET=dev_session_secret
JWT_SECRET=dev_jwt_secret
# Debug logging
LOG_LEVEL=debug
SENTRY_DSN= # Disabled in development
# Development API key (limited quota)
SIMPLYSTREAM_API_KEY=dev_api_key_here
SIMPLYSTREAM_PROJECT_ID=dev_project_id
Staging (.env.staging)
NODE_ENV=production
SIMPLYSTREAM_ENVIRONMENT=staging
# Staging database
DATABASE_URL=${STAGING_DATABASE_URL}
REDIS_URL=${STAGING_REDIS_URL}
# Staging secrets
SESSION_SECRET=${STAGING_SESSION_SECRET}
JWT_SECRET=${STAGING_JWT_SECRET}
# Enable monitoring
SENTRY_DSN=${STAGING_SENTRY_DSN}
LOG_LEVEL=info
# Staging API key
SIMPLYSTREAM_API_KEY=${STAGING_API_KEY}
SIMPLYSTREAM_PROJECT_ID=${STAGING_PROJECT_ID}
Production (.env.production)
NODE_ENV=production
SIMPLYSTREAM_ENVIRONMENT=production
# Production database with SSL
DATABASE_URL=${PRODUCTION_DATABASE_URL}
DATABASE_SSL=true
DATABASE_POOL_SIZE=20
# Production Redis
REDIS_URL=${PRODUCTION_REDIS_URL}
# Production secrets (from secret manager)
SESSION_SECRET=${PRODUCTION_SESSION_SECRET}
JWT_SECRET=${PRODUCTION_JWT_SECRET}
ENCRYPTION_KEY=${PRODUCTION_ENCRYPTION_KEY}
# Full monitoring
SENTRY_DSN=${PRODUCTION_SENTRY_DSN}
NEW_RELIC_LICENSE_KEY=${PRODUCTION_NEW_RELIC_KEY}
LOG_LEVEL=warn
# Production API key
SIMPLYSTREAM_API_KEY=${PRODUCTION_API_KEY}
SIMPLYSTREAM_PROJECT_ID=${PRODUCTION_PROJECT_ID}
# Production settings
API_RATE_LIMIT=100
API_TIMEOUT=30000
Configuration Management
config.ts
import dotenv from 'dotenv';
// Load environment variables
dotenv.config({
path: `.env.${process.env.NODE_ENV || 'development'}`
});
interface Config {
env: string;
port: number;
simplystream: {
apiKey: string;
projectId: string;
environment: string;
};
database: {
url: string;
poolSize: number;
ssl: boolean;
};
redis: {
url: string;
password?: string;
};
security: {
sessionSecret: string;
jwtSecret: string;
};
monitoring: {
sentryDsn?: string;
logLevel: string;
};
}
function validateConfig(): Config {
const required = [
'SIMPLYSTREAM_API_KEY',
'SIMPLYSTREAM_PROJECT_ID',
'DATABASE_URL',
'REDIS_URL',
'SESSION_SECRET',
'JWT_SECRET'
];
const missing = required.filter((key) => !process.env[key]);
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
}
return {
env: process.env.NODE_ENV || 'development',
port: parseInt(process.env.PORT || '3000', 10),
simplystream: {
apiKey: process.env.SIMPLYSTREAM_API_KEY!,
projectId: process.env.SIMPLYSTREAM_PROJECT_ID!,
environment: process.env.SIMPLYSTREAM_ENVIRONMENT || 'development'
},
database: {
url: process.env.DATABASE_URL!,
poolSize: parseInt(process.env.DATABASE_POOL_SIZE || '10', 10),
ssl: process.env.DATABASE_SSL === 'true'
},
redis: {
url: process.env.REDIS_URL!,
password: process.env.REDIS_PASSWORD
},
security: {
sessionSecret: process.env.SESSION_SECRET!,
jwtSecret: process.env.JWT_SECRET!
},
monitoring: {
sentryDsn: process.env.SENTRY_DSN,
logLevel: process.env.LOG_LEVEL || 'info'
}
};
}
export const config = validateConfig();
Usage
import { config } from './config';
// Initialize SimplyStream client
const client = new SimplyStreamClient({
apiKey: config.simplystream.apiKey,
projectId: config.simplystream.projectId,
environment: config.simplystream.environment
});
// Connect to database
const db = await createDatabaseConnection({
url: config.database.url,
ssl: config.database.ssl,
pool: { max: config.database.poolSize }
});
Secret Management
AWS Secrets Manager
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
async function getSecret(secretName: string): Promise<string> {
const client = new SecretsManagerClient({ region: 'us-west-2' });
const command = new GetSecretValueCommand({ SecretId: secretName });
const response = await client.send(command);
return response.SecretString!;
}
// Load secrets at startup
const secrets = {
apiKey: await getSecret('simplystream/api-key'),
dbPassword: await getSecret('database/password'),
jwtSecret: await getSecret('auth/jwt-secret')
};
Google Cloud Secret Manager
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
async function getSecret(name: string): Promise<string> {
const client = new SecretManagerServiceClient();
const [version] = await client.accessSecretVersion({
name: `projects/${PROJECT_ID}/secrets/${name}/versions/latest`
});
return version.payload?.data?.toString() || '';
}
// Usage
const apiKey = await getSecret('simplystream-api-key');
HashiCorp Vault
import vault from 'node-vault';
const client = vault({
apiVersion: 'v1',
endpoint: process.env.VAULT_ADDR,
token: process.env.VAULT_TOKEN
});
async function getSecrets() {
const result = await client.read('secret/data/simplystream');
return result.data.data;
}
// Usage
const secrets = await getSecrets();
const apiKey = secrets.SIMPLYSTREAM_API_KEY;
Environment-Specific Configuration
Feature Flags
export const features = {
enableAnalytics: process.env.ENABLE_ANALYTICS === 'true',
enableCaching: process.env.ENABLE_CACHING !== 'false',
maxUploadSize: parseInt(process.env.MAX_UPLOAD_SIZE || '10485760', 10),
sessionTimeout: parseInt(process.env.SESSION_TIMEOUT || '3600', 10),
// Environment-specific defaults
byEnvironment: {
development: {
enableAnalytics: false,
enableCaching: false,
debug: true
},
staging: {
enableAnalytics: true,
enableCaching: true,
debug: false
},
production: {
enableAnalytics: true,
enableCaching: true,
debug: false
}
}[process.env.NODE_ENV || 'development']
};
Region-Specific Configuration
const regionConfig = {
'us-west-2': {
cdnUrl: 'https://cdn-us-west.yourapp.com',
apiEndpoint: 'https://api-us-west.yourapp.com',
streamRegion: 'us-west-2'
},
'eu-west-1': {
cdnUrl: 'https://cdn-eu.yourapp.com',
apiEndpoint: 'https://api-eu.yourapp.com',
streamRegion: 'eu-west-1'
}
};
export function getRegionConfig(region: string) {
return regionConfig[region] || regionConfig['us-west-2'];
}
Best Practices
1. Never Commit Secrets
# .gitignore
.env
.env.local
.env.*.local
.env.development
.env.staging
.env.production
secrets/
*.pem
*.key
2. Use Environment Variables for Everything
// Good
const apiKey = process.env.SIMPLYSTREAM_API_KEY;
// Bad - hardcoded
const apiKey = 'sk_live_123456789';
3. Validate on Startup
function validateEnvironment() {
const required = ['API_KEY', 'DATABASE_URL', 'REDIS_URL'];
const missing = required.filter((key) => !process.env[key]);
if (missing.length > 0) {
console.error('Missing required environment variables:', missing);
process.exit(1);
}
}
validateEnvironment();
4. Use TypeScript for Type Safety
declare global {
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'staging' | 'production';
PORT: string;
SIMPLYSTREAM_API_KEY: string;
SIMPLYSTREAM_PROJECT_ID: string;
DATABASE_URL: string;
REDIS_URL: string;
}
}
}
5. Document All Variables
Create env.example:
# SimplyStream Configuration
SIMPLYSTREAM_API_KEY=sk_test_your_key_here
SIMPLYSTREAM_PROJECT_ID=proj_your_project_id
SIMPLYSTREAM_ENVIRONMENT=development
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
# Redis
REDIS_URL=redis://localhost:6379
# Security
SESSION_SECRET=generate_random_string_here
JWT_SECRET=generate_random_string_here
Deployment
Docker
# Build-time variables
ARG NODE_ENV=production
ENV NODE_ENV=$NODE_ENV
# Runtime variables from environment
ENV SIMPLYSTREAM_API_KEY=${SIMPLYSTREAM_API_KEY}
ENV DATABASE_URL=${DATABASE_URL}
Kubernetes
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
NODE_ENV: production
PORT: '3000'
---
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
SIMPLYSTREAM_API_KEY: sk_live_xxxxx
DATABASE_URL: postgresql://...
CI/CD
# GitHub Actions
env:
NODE_ENV: production
SIMPLYSTREAM_API_KEY: ${{ secrets.SIMPLYSTREAM_API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
Next Steps
- Review Production Deployment Checklist
- Set up CI/CD Integration
- Configure Docker & Containers