Back to Blog

Dockerizing Full-Stack AI Applications for Zero-Downtime Deployments

DevOpsDockerAWSOptimization

"It works on my machine!"

If you are building full-stack AI applications, you are likely dealing with a messy stack: Node.js for the frontend, Python for the backend, Redis for caching, and maybe Pinecone or PostgreSQL. Managing these dependencies across staging and production environments is a nightmare without containerization.

Here is a blueprint for Dockerizing a Next.js and FastAPI stack for seamless AWS deployments.

1. The Multi-Stage Dockerfile for Next.js

Next.js builds can be heavy. By using a multi-stage Dockerfile, we compile the app in one stage and only copy the compiled assets to the final lightweight Alpine image.

# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: Production Server
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000
CMD ["node", "server.js"]

This reduces the final image size from 1.5GB down to ~150MB, making deployments incredibly fast.

2. Dockerizing FastAPI and Poetry

For Python, the challenge is keeping the image small while compiling C extensions (like numpy or database drivers). Use the python:3.11-slim base image.

FROM python:3.11-slim
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y gcc libpq-dev && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

3. Local Development with Docker Compose

To run the entire stack locally with one command (docker compose up), map your local volumes so you don't have to rebuild the image every time you save a file.

version: '3.8'
services:
  frontend:
    build: 
      context: ./frontend
      target: builder # Use builder stage for dev
    volumes:
      - ./frontend:/app
    ports:
      - "3000:3000"
      
  backend:
    build: ./backend
    volumes:
      - ./backend:/app
    ports:
      - "8000:8000"
    environment:
      - REDIS_URL=redis://redis:6379/0
      
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

4. Deploying to AWS ECS

Once containerized, deploying is simply a matter of pushing the images to AWS Elastic Container Registry (ECR).

Using GitHub Actions, you can configure a pipeline that builds the images on every push to main, pushes them to ECR, and forces an AWS ECS Fargate cluster to redeploy. ECS handles rolling updates automatically, meaning your old containers stay alive serving traffic until the new containers pass their health checks. Zero downtime.