SociApp provides a complete Docker setup for easy deployment with separate containers for backend and frontend services.
Docker Compose Setup
The application uses Docker Compose to orchestrate multiple services:
# docker-compose.yml:1
version : "3.8"
services :
backend :
build : ./backend
container_name : nest-backend
restart : always
env_file :
- ./backend/.env
environment :
- FRONTEND_URL=http://16.171.57.244
networks :
- app-network
ports :
- "3000:3000"
- "3001:3001"
volumes :
- ./backend/uploads:/app/uploads
frontend :
build :
context : ./frontend
args :
- VITE_API_URL=http://16.171.57.244:3000
container_name : vue-frontend
restart : always
ports :
- "80:80"
depends_on :
- backend
networks :
- app-network
networks :
app-network :
driver : bridge
Backend Dockerfile
The backend uses a lightweight Node.js Alpine image:
# backend/dockerfile:1
# Lightweight base image
FROM node:22-alpine
# Working directory inside container
WORKDIR /app
# Copy dependencies first (improves cache)
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the code
COPY . .
# Build project
RUN npm run build
# Expose port
EXPOSE 3000
# Start command
CMD [ "node" , "dist/main.js" ]
Backend Build Process
Base Image
Uses node:22-alpine for a minimal footprint (approximately 180MB vs 1GB for full Node image)
Dependency Installation
Copies package*.json first to leverage Docker layer caching. Dependencies only rebuild when package files change.
Build Stage
Runs npm run build to compile TypeScript to JavaScript in the dist/ directory
Runtime
Executes the compiled application using node dist/main.js
Frontend Dockerfile
The frontend uses a multi-stage build for optimal production size:
# frontend/dockerfile:1
# ---------- STAGE 1: Build ----------
FROM node:22-alpine as build
WORKDIR /app
# Build argument for API URL
ARG VITE_API_URL
ENV VITE_API_URL=$VITE_API_URL
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build-only
# ---------- STAGE 2: Serve ----------
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
# Copy nginx configuration for SPA
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD [ "nginx" , "-g" , "daemon off;" ]
Multi-Stage Build Benefits
The final image only contains the built static files and nginx, not Node.js or build dependencies. Typical size: ~25MB vs 400MB+
The VITE_API_URL argument is injected at build time and baked into the JavaScript bundle.
Nginx serves the static files with optimal performance and SPA routing support.
Environment Variables
Backend Environment
Create a .env file in the backend/ directory:
# Database
DB_HOST = your-database-host
DB_PORT = 3306
DB_USERNAME = your-db-user
DB_PASSWORD = your-db-password
DB_NAME = your-database-name
# JWT Secrets
JWT_ACCESS_SECRET = your-secure-access-secret-here
JWT_REFRESH_SECRET = your-secure-refresh-secret-here
# Email Service
MAIL_HOST = smtp.example.com
MAIL_PORT = 587
MAIL_USER = [email protected]
MAIL_PASSWORD = your-email-password
MAIL_FROM = [email protected]
# Application
NODE_ENV = production
FRONTEND_URL = http://your-domain.com
Never commit .env files to version control. Add them to .gitignore.
Frontend Build Arguments
The frontend requires the API URL at build time:
frontend :
build :
context : ./frontend
args :
- VITE_API_URL=http://your-api-domain.com:3000
Port Configuration
Backend Ports
3000 : Main API server
3001 : Secondary service (if needed)
Frontend Port
80 : HTTP web server (nginx)
Port Mapping
ports :
- "host-port:container-port"
Host Port : Accessible from outside the container
Container Port : Internal port inside the container
Example: "3000:3000" maps container port 3000 to host port 3000.
Volume Mounts
The backend uses a volume for file uploads:
volumes :
- ./backend/uploads:/app/uploads
This ensures uploaded files persist even if the container is recreated.
Volume Benefits
Files survive container restarts and rebuilds
Files are accessible both from the container and host filesystem
Simple to back up by copying the host directory
Networking
Services communicate through a bridge network:
networks :
app-network :
driver : bridge
Internal Communication
Services can reference each other by name:
// Frontend can call backend using container name
const response = await fetch ( 'http://backend:3000/api/stats' );
Deployment Scenarios
Development
Staging
Production
For local development with hot reload: services :
backend :
build : ./backend
volumes :
- ./backend/src:/app/src
environment :
- NODE_ENV=development
command : npm run start:dev
frontend :
build : ./frontend
volumes :
- ./frontend/src:/app/src
command : npm run dev
ports :
- "5173:5173"
Run with: docker-compose -f docker-compose.dev.yml up
For staging environment with production-like setup: services :
backend :
build : ./backend
environment :
- NODE_ENV=staging
- FRONTEND_URL=https://staging.sociapp.com
env_file :
- ./backend/.env.staging
frontend :
build :
context : ./frontend
args :
- VITE_API_URL=https://api-staging.sociapp.com
Run with: docker-compose -f docker-compose.staging.yml up -d
For production with SSL and optimizations: services :
backend :
build : ./backend
restart : always
environment :
- NODE_ENV=production
- FRONTEND_URL=https://sociapp.com
env_file :
- ./backend/.env.production
healthcheck :
test : [ "CMD" , "curl" , "-f" , "http://localhost:3000/health" ]
interval : 30s
timeout : 10s
retries : 3
frontend :
build :
context : ./frontend
args :
- VITE_API_URL=https://api.sociapp.com
restart : always
nginx-proxy :
image : nginx:alpine
ports :
- "443:443"
volumes :
- ./nginx-ssl.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
Run with: docker-compose -f docker-compose.prod.yml up -d
Production Best Practices
1. Use Health Checks
healthcheck :
test : [ "CMD" , "curl" , "-f" , "http://localhost:3000/health" ]
interval : 30s
timeout : 10s
retries : 3
start_period : 40s
2. Set Resource Limits
deploy :
resources :
limits :
cpus : '1'
memory : 512M
reservations :
cpus : '0.5'
memory : 256M
3. Enable Logging
logging :
driver : "json-file"
options :
max-size : "10m"
max-file : "3"
4. Use Secrets for Sensitive Data
secrets :
db_password :
file : ./secrets/db_password.txt
services :
backend :
secrets :
- db_password
5. Implement SSL/TLS
frontend :
environment :
- NGINX_SSL=true
volumes :
- ./ssl/cert.pem:/etc/nginx/ssl/cert.pem:ro
- ./ssl/key.pem:/etc/nginx/ssl/key.pem:ro
Common Commands
Build and Start
# Build images and start containers
docker-compose up --build
# Start in detached mode
docker-compose up -d
# Rebuild specific service
docker-compose build backend
Management
# View logs
docker-compose logs -f
# View logs for specific service
docker-compose logs -f backend
# Stop containers
docker-compose down
# Stop and remove volumes
docker-compose down -v
# Restart a service
docker-compose restart backend
Debugging
# Execute command in running container
docker-compose exec backend sh
# View container stats
docker stats
# Inspect container
docker inspect nest-backend
Troubleshooting
Check logs: docker-compose logs backend
Common issues:
Missing environment variables
Port already in use
Database connection failed
Clear build cache: docker-compose build --no-cache backend
Check disk space: docker system df
docker system prune
Recreate network: docker-compose down
docker network prune
docker-compose up
Fix permissions: sudo chown -R $USER : $USER ./backend/uploads
chmod -R 755 ./backend/uploads
Security Considerations
Environment Variables Store secrets in .env files, never in docker-compose.yml
Non-Root User Run containers as non-root user when possible
Network Isolation Use internal networks for service-to-service communication
Image Updates Regularly update base images for security patches