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
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
```dockerfile

View File

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