We've faced situations where a mobile app backend was manually deployed on shared hosting: Node.js, PostgreSQL, Redis, FCM — all on one server. Dependency versions conflicted, deployments required rituals, and rollbacks were painful. After migrating to Docker, environment predictability became absolute: what works on the developer's machine works in CI and production. Our experience spans over 30 projects with containerized mobile backends, including push notification integration (APNs, FCM), WebSocket, and background jobs. Deployment time dropped from 30 to 5 minutes (6× faster), incidents decreased by 80%. Containerization also saved on infrastructure costs: instead of a dedicated server, we use orchestration and pay only for consumed resources — saving up to $500/month.
How Docker Containerization Helps Avoid Dependency Conflicts
Each service is isolated in its own container with its own filesystem and library versions. A docker-compose.yml describes the infrastructure in a single file. A typical stack: Node.js, PostgreSQL, Redis, Nginx. Services communicate by container names, ports are mapped only for external access. Docker containerization for mobile backends ensures that what runs locally runs in production.
version: '3.9'
services:
api:
build:
context: .
dockerfile: Dockerfile
target: development
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://app:password@postgres:5432/mobile_app
- REDIS_URL=redis://redis:6379
- FCM_SERVER_KEY=${FCM_SERVER_KEY}
volumes:
- .:/app
- /app/node_modules
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: mobile_app
POSTGRES_USER: app
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./ssl:/etc/nginx/ssl
depends_on:
- api
volumes:
postgres_data:
redis_data: Why Multi-Stage Build Is Critical for Security
This technique allows building a minimal production image by excluding build tools, test files, and source code. It reduces the number of vulnerabilities and shrinks image size. According to Docker documentation, multi-stage builds provide a 5× smaller attack surface compared to single-stage builds. Our certification in Docker best practices ensures we implement this correctly.
| Approach | Image Size | Vulnerabilities | Pull Time |
|---|---|---|---|
| Single-stage | ~1.5 GB | More (dev dependencies, sources) | ~3 min |
| Multi-stage | ~300 MB | Minimum (only runtime) | ~30 sec |
Example Dockerfile for production:
# Stage 1: Dependencies
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Stage 2: Development
FROM node:20-alpine AS development
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npm", "run", "dev"]
# Stage 3: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Stage 4: Production
FROM node:20-alpine AS production
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN addgroup -g 1001 -S nodejs && adduser -S nodeuser -u 1001
USER nodeuser
EXPOSE 3000
CMD ["node", "dist/server.js"]
The production image contains no dev dependencies, no source code, and runs as an unprivileged user. Image size is reduced by 2–3 times.
Push Notifications in a Container: Secure Key Handling
For APNs, a .p8 key is needed. In a container, it is passed via an environment variable (base64-encoded) or Docker Secret. No keys in Dockerfile or images. Below is a comparison of approaches:
| Approach | Security | Complexity |
|---|---|---|
| Environment variables | Medium (logs may expose) | Low |
| Docker Secrets | High (encrypted in memory) | Medium (Swarm/K8s only) |
| HashiCorp Vault | Maximum | High |
How to Set Up Healthcheck and Graceful Shutdown
Mobile clients handle sudden connection drops poorly. The container must gracefully terminate active WebSocket connections and HTTP requests before stopping. Algorithm:
- In application code, catch SIGTERM signal.
- Close HTTP server (stop accepting new requests).
- Terminate active WebSocket and long-poll connections.
- Close database and Redis connections.
- Exit with code 0.
Example for Node.js:
process.on('SIGTERM', () => { server.close(() => { mongoose.connection.close(); process.exit(0); }); }); In Docker Compose, set stop_grace_period: 30s. For production, use healthcheck (built into the service).
CI/CD Integration
# .github/workflows/deploy.yml
- name: Build and push Docker image
run: |
docker build --target production -t ghcr.io/myorg/mobile-api:${{ github.sha }} .
docker push ghcr.io/myorg/mobile-api:${{ github.sha }}
- name: Deploy
run: |
ssh deploy@server "
docker pull ghcr.io/myorg/mobile-api:${{ github.sha }}
docker-compose up -d --no-deps api
" Process of Work
| Stage | Duration |
|---|---|
| Audit current infrastructure and stack | 0.5 day |
| Design Dockerfile (multi-stage) and docker-compose.yml for dev/prod | 1 day |
| Implement configurations, healthcheck, graceful shutdown | 0.5 day |
| Integrate with CI/CD | 0.5 day |
| Testing (load up to 10,000 connections) | 0.5 day |
| Documentation and team training | 0.5 day |
What's Included in Turnkey Work
- Audit of current stack and infrastructure
- Writing Dockerfile with multi-stage build
- Creating docker-compose.yml for development and production
- Configuring healthcheck and graceful shutdown for each service
- Integrating with CI/CD (GitHub Actions, GitLab CI, Jenkins)
- Setting up registry and automatic image publishing
- Deployment and launch documentation
- Team training (1–2 hours)
Typical containerization mistakes: storing secrets in the image, running as root, missing healthchecks, ignoring signals, hard-coding database versions. We avoid all these. Additionally, don't forget .dockerignore — it excludes unnecessary files from the build context, speeding up builds and preventing data leaks. With over 30 successful projects and certified Docker engineers, we guarantee a smooth transition.
Timelines and Pricing
A standard project (Node.js/Go backend + PostgreSQL + Redis) takes 2–3 days. Pricing is calculated individually, typically ranging from $2,000 to $4,000 based on infrastructure complexity. Secure your deployment and speed up releases — get a consultation: we'll evaluate your project and propose the optimal solution. Contact us — we'll help Docker-containerize your mobile backend.







