DevOps & Deployment Skill
Skill universal untuk deployment, containerization, CI/CD, dan infrastructure management.
Deployment Platforms
Vercel (Recommended for React/Next.js)
Setup
npm i -g vercel
vercel login
vercel # deploy preview
vercel --prod # deploy production
vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"framework": "vite",
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
],
"headers": [
{
"source": "/assets/(.*)",
"headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
}
]
}
Environment Variables
vercel env add VITE_SUPABASE_URL # per environment
vercel env add VITE_SUPABASE_ANON_KEY
Netlify
netlify.toml
[build]
command = "npm run build"
publish = "dist"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"
VPS (Ubuntu/Debian)
Setup Lengkap
# 1. Install Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# 2. Install PM2
sudo npm install -g pm2
# 3. Clone & build
git clone <repo-url> /var/www/app
cd /var/www/app
npm install
npm run build
# 4. Serve with PM2 (untuk SSR/API)
pm2 start npm --name "app" -- start
pm2 save
pm2 startup
# 5. Atau serve static files dengan Nginx (untuk SPA)
sudo apt install -y nginx
Nginx Config (SPA)
server {
listen 80;
server_name example.com;
root /var/www/app/dist;
index index.html;
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
SSL dengan Certbot
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
# Auto-renew via cron sudah otomatis
Docker
Dockerfile (Node.js App)
# Build stage
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Dockerfile (API/Backend)
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]
docker-compose.yml (Full Stack)
version: '3.8'
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "80:80"
depends_on:
- api
restart: unless-stopped
api:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/appdb
- NODE_ENV=production
depends_on:
- db
restart: unless-stopped
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=appdb
restart: unless-stopped
volumes:
pgdata:
.dockerignore
node_modules
dist
.git
.env
.env.local
*.md
.DS_Store
CI/CD
GitHub Actions — Build & Deploy
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run build
- run: npm test --if-present
# Deploy ke Vercel
- uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
GitHub Actions — PR Check
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main, dev]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run build
- run: npm test --if-present
- run: npx eslint . --if-present
GitLab CI
# .gitlab-ci.yml
stages:
- build
- test
- deploy
build:
stage: build
image: node:20-alpine
script:
- npm ci
- npm run build
artifacts:
paths: [dist/]
test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm test
deploy:
stage: deploy
only: [main]
script:
- npx vercel --prod --token=$VERCEL_TOKEN
Environment Management
Struktur File
.env # Default/shared (commit ke git HANYA jika non-sensitif)
.env.local # Local overrides (JANGAN commit)
.env.development # Dev-specific
.env.production # Production-specific
Naming Convention (Vite)
# Prefix VITE_ agar terekspos ke frontend
VITE_API_URL=https://api.example.com
VITE_SUPABASE_URL=https://xxx.supabase.co
VITE_SUPABASE_ANON_KEY=eyJ...
# Tanpa prefix = hanya server-side
DATABASE_URL=postgresql://...
JWT_SECRET=xxx
.gitignore
.env.local
.env.*.local
.env.production
Monitoring & Health Check
PM2 Monitoring
pm2 monit # Real-time monitoring
pm2 logs app # View logs
pm2 status # List all processes
Health Check Endpoint (Express)
app.get('/health', (req, res) => {
res.json({
status: 'ok',
uptime: process.uptime(),
timestamp: new Date().toISOString(),
version: process.env.npm_package_version,
})
})
Uptime Monitoring
- UptimeRobot (gratis 50 monitors)
- Better Stack (gratis 10 monitors)
- Cek: response time, status code, SSL expiry
Aturan DevOps
- Jangan hardcode secrets — selalu pakai environment variables
- Jangan commit
.env.localatau file berisi credentials - Build harus reproducible — pin dependency versions (
npm ci, bukannpm install) - Setiap deploy harus melalui CI pipeline — jangan deploy manual ke production
- Rollback plan harus ada sebelum deploy
- Zero-downtime deployment jika memungkinkan
- Log semua deployment (siapa, kapan, versi apa)