Skip to main content

Deployment

Docker

API Image

# apps/api/Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build --filter api

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/apps/api/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3001
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD wget -qO- http://localhost:3001/health || exit 1
CMD ["node", "dist/main.js"]

ECS Fargate Task Definition

{
"family": "syncad-api",
"cpu": "512",
"memory": "1024",
"containerDefinitions": [{
"name": "api",
"image": "<ECR_IMAGE>",
"portMappings": [{ "containerPort": 3001 }],
"environment": [
{ "name": "NODE_ENV", "value": "production" }
],
"secrets": [
{ "name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:...:DATABASE_URL" },
{ "name": "JWT_SECRET", "valueFrom": "arn:aws:secretsmanager:...:JWT_SECRET" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": { "awslogs-group": "/ecs/syncad-api", "awslogs-region": "ap-south-1" }
}
}]
}

GitHub Actions — Deploy API

# .github/workflows/deploy-api.yml
name: Deploy API

on:
push:
branches: [main, dev]
paths: ['apps/api/**']

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with: { version: 8 }
- uses: actions/setup-node@v4
with: { node-version: 20, cache: pnpm }

- run: pnpm install --frozen-lockfile
- run: pnpm test --filter api

- name: Build and push Docker image
run: |
aws ecr get-login-password --region ap-south-1 | \
docker login --username AWS --password-stdin ${{ secrets.ECR_REGISTRY }}
docker build -t $ECR_REGISTRY/syncad-api:$GITHUB_SHA -f apps/api/Dockerfile .
docker push $ECR_REGISTRY/syncad-api:$GITHUB_SHA
docker tag $ECR_REGISTRY/syncad-api:$GITHUB_SHA $ECR_REGISTRY/syncad-api:latest
docker push $ECR_REGISTRY/syncad-api:latest

- name: Deploy to ECS
run: |
aws ecs update-service \
--cluster syncad \
--service api \
--force-new-deployment

GitHub Actions — Deploy UIs

# .github/workflows/deploy-ui.yml
name: Deploy School Admin UI

on:
push:
branches: [main, dev]
paths: ['apps/school-admin-ui/**']

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
- uses: actions/setup-node@v4
with: { node-version: 20, cache: pnpm }

- run: pnpm install --frozen-lockfile
- run: pnpm build --filter school-admin-ui

- name: Deploy to S3 + CloudFront
run: |
aws s3 sync apps/school-admin-ui/.next/static s3://$S3_BUCKET/static \
--delete --cache-control "max-age=31536000,immutable"
aws cloudfront create-invalidation --distribution-id $CF_ID --paths "/*"

Environment-Specific Deployments

BranchEnvironmentURL
devDevelopmentdev-api.metaonus.in
mainStagingstaging-api.metaonus.in
Tag v*.*.*Productionapi.metaonus.in