Deploying Prompt-Optimizer Using Docker: Environment Variable Management and Production Best Practices
Build a multi-stage Docker image for prompt-optimizer, externalize all secrets via .env files or Docker secrets, and orchestrate services using the provided docker-compose.yml for secure, reproducible deployments.
Prompt-optimizer is a multi-package TypeScript project designed to optimize and manage AI prompts. When deploying prompt-optimizer using Docker, implementing proper environment variable management and following containerization best practices ensures your API keys remain secure and your deployments remain consistent across development and production environments.
Building a Production-Ready Docker Image
The repository's Dockerfile defines the container build process using Node.js 20 on Alpine Linux for a minimal footprint. Understanding each layer helps optimize the final image size and security posture.
Understanding the Dockerfile Structure
Located at Dockerfile in the repository root, the build process follows these steps:
| Step | Instruction | Purpose |
|---|---|---|
| Base Image | FROM node:20-alpine |
Uses lightweight Alpine Linux with Node.js LTS to minimize attack surface. |
| Working Directory | WORKDIR /app |
Establishes a consistent path for all subsequent operations. |
| Dependency Caching | COPY package.json pnpm-lock.yaml ./ |
Copies lockfiles first to leverage Docker layer caching. |
| Package Installation | RUN corepack enable && pnpm install --frozen-lockfile |
Installs exact dependency versions using pnpm via Corepack. |
| Source Copy | COPY . . |
Adds application source code after dependency layer is cached. |
| Build Step | RUN pnpm build |
Compiles TypeScript to JavaScript using the monorepo build script. |
| Port Exposure | EXPOSE 3000 |
Documents the port the web interface listens on. |
| Runtime Command | CMD ["node", "packages/web/dist/server.js"] |
Starts the compiled web server as the container entry point. |
Source: Dockerfile on GitHub
Optimizing with Multi-Stage Builds
To further reduce image size and eliminate build-time dependencies from the final artifact, implement a multi-stage build. This approach compiles the application in a builder stage, then copies only the necessary artifacts to a runtime stage.
# ---- Builder stage -------------------------------------------------
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build
# ---- Runtime stage -------------------------------------------------
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/packages/web/dist ./dist
COPY --from=builder /app/package.json .
RUN corepack enable && pnpm install --prod --frozen-lockfile
EXPOSE 3000
CMD ["node", "dist/server.js"]
This configuration ensures that TypeScript compilers, development dependencies, and source maps remain in the builder stage, while the final image contains only the compiled JavaScript and production dependencies.
Managing Environment Variables and Secrets
Proper configuration management separates environment-specific values from the container image, preventing sensitive data from being baked into layers that could be extracted later.
Using .env Files for Local Development
The repository includes an .env.example file that documents required variables. For local deployments:
-
Copy the template to create a local environment file:
cp .env.example .env -
Populate sensitive values such as API keys:
# .env APP_PORT=3000 OPENAI_API_KEY=sk-... REDIS_URL=redis://redis:6379 -
Reference in Docker Compose via the
env_filedirective (see the Orchestration section).
Implementing Docker Secrets for Production
For production environments using Docker Swarm or Kubernetes, Docker secrets provide a more secure alternative to environment variables. Secrets are mounted as files in /run/secrets/ and never appear in the container's process environment list, reducing the risk of leakage through /proc or debugging tools.
secrets:
openai_api_key:
external: true
services:
app:
secrets:
- openai_api_key
The application reads the secret from the environment variable when Docker injects it, requiring no code changes if already using process.env.OPENAI_API_KEY.
Avoiding Hard-Coded Configuration
Ensure the application entry point (typically packages/web/src/server.ts or similar) reads configuration dynamically:
export const CONFIG = {
port: Number(process.env.APP_PORT ?? 3000),
openaiKey: process.env.OPENAI_API_KEY ?? '',
redisUrl: process.env.REDIS_URL ?? 'redis://localhost:6379',
};
This pattern allows the same image to run in development, staging, and production without rebuilding, simply by changing the injected environment.
Orchestrating with Docker Compose
The repository's docker-compose.yml defines the complete runtime environment, linking the application with reverse proxies and optional data stores.
Understanding the Compose Configuration
The standard configuration includes three primary services:
| Service | Image | Purpose | Key Configuration |
|---|---|---|---|
| app | Built from Dockerfile |
Runs the compiled Prompt-Optimizer server. | env_file: .env, ports: - "${APP_PORT}:3000", restart: unless-stopped |
| nginx | nginx:stable-alpine |
Static asset serving and reverse proxy to app. |
volumes: - ./docker/nginx.conf:/etc/nginx/nginx.conf:ro |
| redis | redis:7-alpine |
Optional caching/session store. | volumes: - redis-data:/data |
Source: docker-compose.yml on GitHub
Health Checks and Restart Policies
Production deployments should include health checks to ensure the container is actually serving traffic, not just running:
services:
app:
build: .
env_file:
- .env
ports:
- "${APP_PORT:-3000}:3000"
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
- redis
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
restart: unless-stopped
nginx:
image: nginx:stable-alpine
ports:
- "80:80"
volumes:
- ./docker/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- app
restart: unless-stopped
volumes:
redis-data:
This configuration ensures that if the application fails to respond to the health endpoint, Docker marks the container as unhealthy and can trigger restart policies or orchestrator migrations.
Production Deployment Checklist
Before promoting your container to production, verify the following criteria:
| ✅ Checklist Item | How to Verify |
|---|---|
| Image size ≤ 200 MB | Run docker images and check the repository size. |
| No dev dependencies in final image | Execute docker run --rm -it <image> sh -c "npm ls --prod" to confirm only production packages exist. |
| Environment variables injected at runtime | Ensure docker-compose up reads from .env without requiring image rebuilds. |
| Health endpoint responding | curl http://localhost:3000/health must return HTTP 200. |
| Graceful shutdown handling | docker compose stop should send SIGTERM and allow the Node process to exit cleanly without data loss. |
| Logs directed to stdout | docker compose logs -f app should show application output; verify no log files are written inside the container. |
| Secrets not exposed in image layers | Run docker inspect <image> and confirm OPENAI_API_KEY is not present in environment variables. |
| Automatic rebuild on code changes | Modify source code and run docker compose up --build; verify only changed layers rebuild. |
Summary
Deploying prompt-optimizer using Docker requires attention to image optimization, secret management, and orchestration configuration. Key takeaways include:
- Use multi-stage builds to separate compilation dependencies from runtime artifacts, significantly reducing image size and attack surface.
- Externalize all configuration via
.envfiles for development and Docker secrets for production, ensuring sensitive API keys never exist in image layers. - Leverage the provided
docker-compose.ymlto orchestrate the application with nginx and optional Redis, implementing health checks and restart policies for resilience. - Verify production readiness using the deployment checklist to confirm image size, absence of dev dependencies, and proper secret handling before exposing to traffic.
Frequently Asked Questions
How do I build the prompt-optimizer Docker image for production?
Build the image using the repository's Dockerfile with a multi-stage approach to exclude development dependencies. Run docker build -t prompt-optimizer:latest . from the repository root, or use docker compose build if managing the full stack. Ensure you use the --production flag or a separate runtime stage to minimize the final image size and remove build tools.
Where should I store API keys when deploying prompt-optimizer?
Never embed API keys in the Docker image. For local development, create a .env file based on .env.example and mount it via the env_file directive in docker-compose.yml. For production deployments using Docker Swarm or Kubernetes, use Docker secrets mounted as files in /run/secrets/, which prevents keys from appearing in environment variable dumps or process listings.
Can I run prompt-optimizer without Docker Compose?
Yes, you can run the container directly using docker run commands, though Docker Compose is recommended for managing multi-service dependencies like nginx and Redis. If running standalone, expose port 3000 and mount your environment file: docker run -p 3000:3000 --env-file .env prompt-optimizer:latest. Ensure you still externalize all configuration and avoid baking secrets into the image.
How do I verify my prompt-optimizer deployment is secure?
Verify security by checking that the final image contains no development dependencies using docker run --rm -it <image> sh -c "npm ls --prod". Confirm that sensitive variables like OPENAI_API_KEY are not present in the image layers by inspecting docker inspect <image>. Additionally, ensure the health check endpoint responds correctly at /health and that logs are directed to stdout rather than files inside the container.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →