Most projects start the same way: a docker-compose.yml that works perfectly on your laptop. Then someone says "let's deploy this" and suddenly you're dealing with SSL certificates, health checks, secrets management, and cost optimization. Here's the checklist I follow.
1. Separate Configuration from Code
Every environment-specific value — database URLs, API keys, feature flags — lives in environment variables, not config files. I use pydantic-settings in Python and $env/dynamic/private in SvelteKit. The same container image runs in dev, staging, and production with different env vars.
2. Health Checks and Graceful Shutdown
Every service gets a /health endpoint. Not just "is the process running" but "can this service actually handle requests" — database connectivity, external API reachability, memory availability. The orchestrator (whether it's Docker Compose, App Platform, or Kubernetes) uses this to route traffic only to healthy instances.
3. Database Migrations as a Deployment Step
Migrations run before the application starts, guarded by a flag (RUN_MIGRATIONS=1). This prevents race conditions when running multiple replicas — only one instance should run migrations. I use Alembic for Python projects with idempotent migrations that are safe to re-run.
4. SSL and Security Headers
In production, every response includes security headers: X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin. For Cloudflare deployments, these go in a _headers file. For traditional setups, Caddy or nginx handles them.
5. Connection Pooling for Managed Databases
Serverless databases like Neon close idle connections aggressively. Your connection pool needs pool_pre_ping=True to verify connections before use, and pool_recycle=1800 to refresh connections before the server-side timeout hits. Without this, you'll get intermittent "connection reset" errors that are maddening to debug.
6. Cost Optimization from Day One
Cloud costs compound quickly. My rules: use the smallest instance that passes load testing, start with shared/burstable CPU (most services are idle 95% of the time), and monitor actual resource usage before scaling up. A $12/month Digital Ocean app platform instance runs most small-to-medium services without breaking a sweat.
Production doesn't have to be complicated. It just has to be intentional.