feat: add official Docker image with GHCR publishing workflow

- Add docker/Dockerfile with non-root user and configurable environment
- Add GitHub Actions workflow to build multi-platform images (amd64/arm64)
- Publish to ghcr.io/benoitc/gunicorn on version tags
- Update documentation with official image usage examples
This commit is contained in:
Benoit Chesneau 2026-01-23 18:57:21 +01:00
parent 7894d1c170
commit 469110d647
6 changed files with 206 additions and 2 deletions

57
.github/workflows/docker-publish.yml vendored Normal file
View File

@ -0,0 +1,57 @@
name: Docker Publish
on:
push:
tags:
- 'v*'
workflow_dispatch:
permissions:
contents: read
packages: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
name: Build and Push Docker Image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

8
docker/.dockerignore Normal file
View File

@ -0,0 +1,8 @@
.git
.github
__pycache__
*.pyc
.pytest_cache
.tox
docs
tests

31
docker/Dockerfile Normal file
View File

@ -0,0 +1,31 @@
FROM python:3.12-slim
LABEL org.opencontainers.image.source=https://github.com/benoitc/gunicorn
LABEL org.opencontainers.image.description="Gunicorn Python WSGI HTTP Server"
LABEL org.opencontainers.image.licenses=MIT
# Create non-root user
RUN useradd --create-home --shell /bin/bash gunicorn
WORKDIR /app
# Install gunicorn
RUN pip install --no-cache-dir gunicorn
# Copy entrypoint script
COPY docker/docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Configuration via environment:
# GUNICORN_BIND - full bind address (default: [::]:8000, IPv4+IPv6)
# GUNICORN_HOST - bind host (default: [::])
# GUNICORN_PORT - bind port (default: 8000)
# GUNICORN_WORKERS - number of workers (default: number of CPUs)
# GUNICORN_ARGS - additional arguments (e.g., "--timeout 120")
USER gunicorn
EXPOSE 8000
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["--help"]

View File

@ -0,0 +1,33 @@
#!/bin/bash
set -e
# Allow running other commands (e.g., bash for debugging)
if [ "${1:0:1}" = '-' ] || [ -z "${1##*:*}" ]; then
# First arg is a flag or contains ':' (app:callable), run gunicorn
# Build bind address from GUNICORN_HOST and GUNICORN_PORT, or use GUNICORN_BIND
# Default: listen on both IPv4 and IPv6
PORT="${GUNICORN_PORT:-8000}"
BIND="${GUNICORN_BIND:-${GUNICORN_HOST:-[::]}:${PORT}}"
# Add bind if not specified in args or GUNICORN_ARGS
if [[ ! " $* $GUNICORN_ARGS " =~ " --bind " ]] && [[ ! " $* $GUNICORN_ARGS " =~ " -b " ]] && [[ ! "$* $GUNICORN_ARGS" =~ --bind= ]] && [[ ! "$* $GUNICORN_ARGS" =~ -b= ]]; then
set -- --bind "$BIND" "$@"
fi
# Add workers if not specified - default to number of CPUs
if [[ ! " $* $GUNICORN_ARGS " =~ " --workers " ]] && [[ ! " $* $GUNICORN_ARGS " =~ " -w " ]] && [[ ! "$* $GUNICORN_ARGS" =~ --workers= ]] && [[ ! "$* $GUNICORN_ARGS" =~ -w= ]]; then
WORKERS="${GUNICORN_WORKERS:-$(nproc)}"
set -- --workers "$WORKERS" "$@"
fi
# Append GUNICORN_ARGS if set
if [ -n "$GUNICORN_ARGS" ]; then
exec gunicorn $GUNICORN_ARGS "$@"
fi
exec gunicorn "$@"
fi
# Otherwise, run the command as-is (e.g., bash, sh, python)
exec "$@"

View File

@ -4,6 +4,81 @@ Running Gunicorn in Docker containers is the most common deployment pattern
for modern Python applications. This guide covers best practices for for modern Python applications. This guide covers best practices for
containerizing Gunicorn applications. containerizing Gunicorn applications.
## Official Docker Image
Gunicorn provides an official Docker image on GitHub Container Registry:
```bash
docker pull ghcr.io/benoitc/gunicorn:latest
```
### Quick Start
Mount your application directory and run:
```bash
docker run -p 8000:8000 -v $(pwd):/app ghcr.io/benoitc/gunicorn app:app
```
### Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `GUNICORN_BIND` | Full bind address | `[::]:8000` (IPv4+IPv6) |
| `GUNICORN_HOST` | Bind host | `[::]` |
| `GUNICORN_PORT` | Bind port | `8000` |
| `GUNICORN_WORKERS` | Number of workers | Number of CPUs |
| `GUNICORN_ARGS` | Additional arguments | (none) |
### With Configuration
```bash
docker run -p 9000:9000 -v $(pwd):/app \
-e GUNICORN_PORT=9000 \
-e GUNICORN_WORKERS=4 \
-e GUNICORN_ARGS="--timeout 120 --access-logfile -" \
ghcr.io/benoitc/gunicorn app:app
```
### As Base Image (Recommended for Production)
```dockerfile
FROM ghcr.io/benoitc/gunicorn:24.1.0
# Install app dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY --chown=gunicorn:gunicorn . .
CMD ["myapp:app", "--workers", "4"]
```
### With Docker Compose
```yaml
services:
web:
image: ghcr.io/benoitc/gunicorn:latest
ports:
- "8000:8000"
volumes:
- ./app:/app
command: ["myapp:app", "--workers", "4"]
```
### Available Tags
- `ghcr.io/benoitc/gunicorn:latest` - Latest release
- `ghcr.io/benoitc/gunicorn:24.1.0` - Specific version
- `ghcr.io/benoitc/gunicorn:24.1` - Minor version
- `ghcr.io/benoitc/gunicorn:24` - Major version
## Building Your Own Image
For more control, build a custom image using the patterns below.
## Basic Dockerfile ## Basic Dockerfile
```dockerfile ```dockerfile

View File

@ -20,8 +20,8 @@
=== "Docker" === "Docker"
```bash ```bash
docker run -p 8000:8000 -v $(pwd):/app -w /app \ docker pull ghcr.io/benoitc/gunicorn:latest
python:3.12-slim sh -c "pip install gunicorn && gunicorn app:app" docker run -p 8000:8000 -v $(pwd):/app ghcr.io/benoitc/gunicorn app:app
``` ```
See the [Docker guide](guides/docker.md) for production configurations. See the [Docker guide](guides/docker.md) for production configurations.