Setting Up Docker Containerization for Mobile App Backend
A mobile app backend is not just a REST API. It includes push notifications via FCM/APNs, WebSocket for chat, background tasks for media processing, Redis for caching sessions. Running everything on a single server as needed, library versions conflict, and deployment becomes a matter of "hoping it won't break." Docker solves environment predictability: what works for a developer works in CI and production.
Structure for a Typical Mobile Backend
The most common stack: Node.js / Go / Python + PostgreSQL + Redis + Nginx. docker-compose.yml for local development:
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:
Multi-stage Dockerfile
One Dockerfile for development and production via multi-stage build:
# 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 not as root
RUN addgroup -g 1001 -S nodejs && adduser -S nodeuser -u 1001
USER nodeuser
EXPOSE 3000
CMD ["node", "dist/server.js"]
The production image doesn't contain dev dependencies, source code, and runs as an unprivileged user. Image size decreases by 2–3 times compared to a naive approach.
Push Notifications in Container
For APNs (Apple Push Notification service), you need a .p8 key. In a container, it's passed via environment variable (base64-encoded) or Docker Secret (in Swarm/Kubernetes). No keys in the Dockerfile or images.
# Encoding
APNS_KEY_BASE64=$(base64 -w 0 AuthKey_XXXXXX.p8)
# In docker-compose via secrets or env:
APNS_KEY_BASE64=${APNS_KEY_BASE64}
APNS_KEY_ID=XXXXXXXXXX
APNS_TEAM_ID=YYYYYYYYYY
Healthcheck and Graceful Shutdown
Mobile clients don't handle sudden connection drops well. The container should properly close active WebSocket connections and HTTP requests before stopping. Node.js:
process.on('SIGTERM', () => {
server.close(() => {
mongoose.connection.close();
process.exit(0);
});
});
In Docker Compose/Kubernetes, stop_grace_period: 30s gives the container time to shut down.
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
Audit current deployment → write Dockerfile (multi-stage) → write docker-compose.yml for dev and prod → set up healthcheck → integrate with CI → configure registry → test deployment → write documentation.
Timeline: 2–3 days for a standard Node.js/Go backend. Cost is calculated individually after studying the stack and infrastructure.







