Before You Deploy: 15 Production Checks
Moving a containerized application from development to production introduces a class of failures that don't show up locally. This checklist covers the most common issues teams discover the hard way.
1. Pin Your Base Image Version
# Never do this in production
FROM node:latest
# Do this
FROM node:20.13-alpine3.19latest changes under you. Pin to a specific version for reproducible builds.
2. Use a Non-Root User
FROM node:20-alpine
WORKDIR /app
COPY --chown=node:node . .
RUN npm ci --only=production
USER node
CMD ["node", "server.js"]Running as root inside a container is a security risk. Always switch to a non-root user before CMD.
3. Set Memory and CPU Limits
deploy:
resources:
limits:
cpus: '1.0'
memory: 512MNo limits = a memory leak can take down every service on the host.
4. Add a HEALTHCHECK
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD wget -qO- http://localhost:3000/health || exit 1Without a healthcheck, orchestrators can't tell if your app is actually ready to serve traffic.
5. Use Multi-Stage Builds
Keep build tools and dev dependencies out of your final image:
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
CMD ["node", "dist/server.js"]6. Have a .dockerignore File
node_modules
.git
.env
*.log
coverage
.DS_StoreWithout it, you're sending gigabytes to the Docker daemon and leaking .env files into your image.
7. Never Bake Secrets into Images
Every RUN command, every COPY is a layer. Secrets in layers persist even after deletion.
# BAD — DATABASE_URL is visible in docker history
RUN export DATABASE_URL="postgres://..."
# GOOD — inject at runtime
docker run -e DATABASE_URL="postgres://..." my-app:latestOn PandaStack, set secrets from [dashboard.pandastack.io](https://dashboard.pandastack.io) — they're injected at runtime, never baked in.
8. Implement Graceful Shutdown
Handle SIGTERM in your application so in-flight requests complete before the container stops:
process.on('SIGTERM', () => {
server.close(() => {
console.log('Graceful shutdown complete');
process.exit(0);
});
});Without this, rolling deployments drop active connections.
9. Use exec Form for CMD
# Shell form — shell intercepts signals, app never gets SIGTERM
CMD "node server.js"
# Exec form — signals go directly to the app process
CMD ["node", "server.js"]10. Set Restart Policies
services:
api:
image: my-app:latest
restart: unless-stoppedOptions: no, always, on-failure, unless-stopped. Use unless-stopped for services that should always be running.
11. Scan Images for CVEs
docker scout cves my-app:latest
# or
trivy image my-app:latestRun scans in CI before pushing to production. Rebuild with updated base images regularly.
12. Configure Structured Logging
Don't write logs to files inside containers — write to stdout/stderr. The container runtime captures them:
// Use JSON structured logging
console.log(JSON.stringify({ level: 'info', msg: 'Server started', port: 3000 }));docker logs --tail 100 -f my-container13. Use Read-Only Filesystem Where Possible
docker run --read-only --tmpfs /tmp:rw,noexec,nosuid my-app:latestA read-only filesystem prevents attackers from writing malicious files if they get code execution.
14. Tag Images with Commit SHAs
# Don't use 'latest' in production — it's not auditable
docker build -t my-app:$GIT_SHA .
docker push my-app:$GIT_SHATag with the git commit SHA so you can always trace a running container back to the exact code.
15. Test Your Rollback Process
# Know how to roll back before you need to
docker service update --image my-app:previous-sha my-app-serviceDeploys fail. Practice the rollback before your first production incident.
Deploying Production-Ready Containers on PandaStack
PandaStack's container deployments handle many of these concerns at the platform level — TLS termination, health checks, rolling deployments, and restart policies are managed for you.
npm install -g @pandastack/cli
panda deploy --type container --repo your-org/your-repoFocus on writing great code; let the platform handle the infrastructure. Learn more at [docs.pandastack.io](https://docs.pandastack.io) or manage your deployments at [dashboard.pandastack.io](https://dashboard.pandastack.io).