Docker¶
ProcessPype ships with a production-ready multi-stage Dockerfile and a docker-compose.yml for local development and testing.
Dockerfile¶
The included Dockerfile uses a two-stage build:
Builder stage --- installs build dependencies, all Python packages, and the application using uv.
Runtime stage --- copies only the installed packages and application code into a minimal python:3.13-slim image. The application runs as a non-root user (appuser) and uses tini for proper signal handling.
# Build stage
FROM python:3.13-slim as builder
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends build-essential curl && rm -rf /var/lib/apt/lists/*
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
COPY pyproject.toml uv.lock ./
RUN uv sync --no-dev --no-install-project
COPY . .
RUN uv sync --no-dev --extra all_py313
# Runtime stage
FROM python:3.13-slim as runtime
WORKDIR /app
RUN apt-get update && apt-get install -y --no-install-recommends curl tini && rm -rf /var/lib/apt/lists/*
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY --from=builder /app/processpype /app/processpype
COPY --from=builder /app/pyproject.toml /app/
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
ENV PYTHONPATH=/app PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 PORT=8000 APP_HOST=0.0.0.0 APP_PORT=8000 APP_ENV=production APP_DEBUG=false ENABLED_SERVICES=monitoring LOGFIRE_KEY=
EXPOSE 8000
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["python", "-m", "uvicorn", "processpype.main:app.api", "--host", "0.0.0.0", "--port", "8000", "--timeout-keep-alive", "75", "--log-level", "info", "--timeout-graceful-shutdown", "10", "--no-access-log", "--use-colors"]
Environment Variables¶
Configure the application via environment variables when running in Docker. The primary configuration mechanism is YAML files with ${ENV_VAR} token replacement, but the following environment variables are recognized directly:
| Variable | Default | Description |
|---|---|---|
APP_HOST |
"0.0.0.0" |
Bind host |
APP_PORT |
"8000" |
Bind port |
APP_DEBUG |
"false" |
Enable debug mode |
APP_ENV |
"production" |
Environment name |
LOGFIRE_KEY |
(none) | Logfire API token |
ENABLED_SERVICES |
"" |
Comma-separated list of services to auto-register from the registry |
Building the Image¶
# Build the image
docker build -t processpype:latest .
# Build with a specific target (builder or runtime)
docker build --target runtime -t processpype:latest .
Running with Docker¶
# Run the application
docker run -p 8000:8000 \
-e APP_ENV=production \
-e ENABLED_SERVICES=monitoring \
processpype:latest
# Run with Logfire enabled
docker run -p 8000:8000 \
-e LOGFIRE_KEY=your-token \
-e ENABLED_SERVICES=monitoring \
processpype:latest
Docker Compose¶
The included docker-compose.yml provides three service profiles:
Development (app)¶
Mounts the local processpype/ directory as a volume for hot-reloading during development:
services:
app:
build:
context: .
dockerfile: Dockerfile
target: runtime
ports:
- "8000:8000"
environment:
- PYTHONPATH=/app
- PYTHONUNBUFFERED=1
- APP_TITLE=ProcessPype Dev
- APP_ENV=development
- APP_DEBUG=true
- ENABLED_SERVICES=monitoring
- LOGFIRE_KEY=
volumes:
- ./processpype:/app/processpype
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
stop_grace_period: 10s
docker compose up app
Production (app-prod)¶
No volume mount, runs with APP_ENV=production and multiple services:
services:
app-prod:
build:
context: .
dockerfile: Dockerfile
target: runtime
ports:
- "8001:8000"
environment:
- APP_TITLE=ProcessPype Production
- APP_ENV=production
- APP_DEBUG=false
- ENABLED_SERVICES=monitoring,clock
- LOGFIRE_KEY=
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
stop_grace_period: 10s
docker compose up app-prod
Testing (test)¶
Runs the test suite inside the builder stage:
services:
test:
build:
context: .
dockerfile: Dockerfile
target: builder
volumes:
- .:/app
environment:
- PYTHONPATH=/app
- PYTHONUNBUFFERED=1
- APP_ENV=test
- APP_DEBUG=true
- LOGFIRE_KEY=
command: uv run pytest
docker compose run test
Health Check¶
Both app and app-prod services include a health check that polls http://localhost:8000/health. Ensure your application responds to this endpoint, or adjust the health check URL to match your api_prefix and routing setup.
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
Graceful Shutdown¶
tini is used as the container init process to properly forward SIGTERM and SIGINT signals to the Python process. The ApplicationCreator sets up signal handlers that trigger Uvicorn's graceful shutdown when these signals are received.
The stop_grace_period in docker-compose and --timeout-graceful-shutdown in Uvicorn control how long to wait for in-flight requests and services to stop before forceful termination.
Custom Entry Point¶
To use a custom application class or register additional services at startup, create your own entry point instead of using processpype.main:
# myapp/main.py
from processpype.creator import ApplicationCreator
from processpype.application import Application
from processpype import ProcessPypeConfig
from myapp.services import MyCustomService
class MyApplicationCreator(ApplicationCreator):
@classmethod
def get_application(cls, **kwargs) -> Application:
config = ProcessPypeConfig(server={"host": "0.0.0.0", "port": 8080})
app = Application(config)
cls.app = app
cls._setup_startup_callback()
cls._setup_shutdown_callback()
return app
app = MyApplicationCreator.get_application()
Then update the Dockerfile CMD:
CMD ["python", "-m", "uvicorn", "myapp.main:app.api", "--host", "0.0.0.0", "--port", "8000"]