Skip to content

Instantly share code, notes, and snippets.

@goten002
Last active October 26, 2025 23:23
Show Gist options
  • Select an option

  • Save goten002/d7c2c24a6d94b77fe23b2f1d47dcd418 to your computer and use it in GitHub Desktop.

Select an option

Save goten002/d7c2c24a6d94b77fe23b2f1d47dcd418 to your computer and use it in GitHub Desktop.
Optimized Dockerfile for deploying a FastAPI application using Gunicorn and UvicornWorker. Includes best practices like a non-root user, UV package manager for dependency management, and multi-stage builds using alpine for smaller images.
FROM python:3.13-alpine AS base
# Set environment variables for Python runtime behavior
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
APP_DIR="/app"
# Define build arguments for non-root user/group IDs (defaults provided)
ARG APP_USER=appuser
ARG APP_UID=1001
ARG APP_GID=1001
# Create the non-root group and user for security, setting APP_DIR as the home directory
RUN addgroup -g $APP_GID $APP_USER \
&& adduser -h $APP_DIR -u $APP_UID -G $APP_USER -D $APP_USER
# Set working directory to the application root
WORKDIR $APP_DIR
# ------------------------
# Builder stage: Install system dependencies (optional) and project Python dependencies
# Inherits base settings (Python, paths, user args)
# ------------------------
FROM base AS builder
# Set environment variables specifically for uv operations
ENV UV_CACHE_DIR="/root/.cache/uv" \
UV_PROJECT="$APP_DIR" \
UV_FROZEN="true" \
UV_VENV_SEED="false"
# Install system packages potentially needed for building Python extensions (e.g., C compilers)
# This is optional if all your Python dependencies are pure Python wheels.
RUN apk update \
&& apk add --no-cache \
build-base \
&& rm -rf /var/cache/apk/*
# Copy the uv and uvx executables from an official uv image directly into the builder's /bin
# This makes 'uv' available without needing the full base image or installer script.
COPY --from=ghcr.io/astral-sh/uv /uv /uvx /bin/
# Install Python dependencies using uv sync
RUN --mount=type=cache,target=$UV_CACHE_DIR \
--mount=type=bind,source=uv.lock,target=uv.lock,ro \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml,ro \
uv sync --no-install-project
# ------------------------
# Production stage: Build the final lean image
# Inherits base settings (Python, paths, user args) - DOES NOT inherit builder contents directly
# ------------------------
FROM base AS production
# Copy the application code from the build context into the image
# Set ownership to the non-root user defined earlier
COPY --chown=$APP_UID:$APP_GID main_package $APP_DIR/main_package
# Alternatively, in case of src project structure
#COPY --chown=$APP_UID:$APP_GID src $APP_DIR/src
# Copy the populated virtual environment (with installed dependencies) from the builder stage
COPY --from=builder $APP_DIR/.venv $APP_DIR/.venv
# Add the virtual environment's bin directory to the PATH
ENV PATH="$APP_DIR/.venv/bin:$PATH"
# Switch to the non-root user to run the application
USER $APP_UID:$APP_GID
# Expose the port the application will listen on (default for FastAPI/Gunicorn)
EXPOSE 8000
# Define the command to run the application using Gunicorn
# Gunicorn and uvicorn must be listed as dependencies in pyproject.toml (and thus uv.lock)
# The PATH variable (set in base stage) ensures 'gunicorn' is found in the venv's bin directory.
CMD ["gunicorn", "-b", "0.0.0.0:8000", "-k", "uvicorn.workers.UvicornWorker", "main_package.app:app"]
# Alternatively, in case of src project structure
#CMD ["gunicorn", "--chdir", "src", "-b", "0.0.0.0:8000", "-k", "uvicorn.workers.UvicornWorker", "main_package.app:app"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment