Claude
Skills
Sign in
Back

flask-docker-deployment

Included with Lifetime
$97 forever

Set up Docker deployment for Flask applications with Gunicorn, automated versioning, and container registry publishing. Use when dockerizing a Flask app, containerizing for production, or setting up CI/CD with Docker.

Cloud & DevOps

What this skill does


# Flask Docker Deployment Pattern

This skill helps you containerize Flask applications using Docker with Gunicorn for production, automated version management, and seamless container registry publishing.

## When to Use This Skill

Use this skill when:
- You have a Flask application ready to deploy
- You want production-grade containerization with Gunicorn
- You need automated version management for builds
- You're publishing to a container registry (Docker Hub, GHCR, ECR, etc.)
- You want a repeatable, idempotent deployment pipeline

## What This Skill Creates

1. **Dockerfile** - Multi-stage production-ready container with security best practices
2. **build-publish.sh** - Automated build script with version management
3. **VERSION** file - Auto-incrementing version tracking (gitignored)
4. **.gitignore** - Entry for VERSION file
5. **Optional .dockerignore** - Exclude unnecessary files from build context

## Prerequisites

Before using this skill, ensure:
1. Flask application is working locally
2. `requirements.txt` exists with all dependencies
3. Docker is installed and running
4. You're authenticated to your container registry (if publishing)

## Step 1: Gather Project Information

**IMPORTANT**: Before creating files, ask the user these questions:

1. **"What is your Flask application entry point?"**
   - Format: `{module_name}:{app_variable}`
   - Example: `flask_app:app` or `api_server:create_app()`

2. **"What port does your Flask app use?"**
   - Pick a random port above 5000 (e.g., 5678, 6100, 7200) — avoid well-known ports
   - Do NOT default to 5000

3. **"What is your container registry URL?"**
   - Examples:
     - GitHub: `ghcr.io/{org}/{project}`
     - Docker Hub: `docker.io/{user}/{project}`
     - AWS ECR: `{account}.dkr.ecr.{region}.amazonaws.com/{project}`

4. **"Do you have private Git dependencies?"** (yes/no)
   - If yes: Will need GitHub Personal Access Token (CR_PAT)
   - If no: Can skip git installation step

5. **"How many Gunicorn workers do you want?"**
   - Default: 4
   - Recommendation: 2-4 × CPU cores
   - Note: For background job workers, use 1

## Step 2: Create Dockerfile

Create `Dockerfile` in the project root:

```dockerfile
FROM python:3.13-slim

# Build-time token for cloning private GitHub deps. ARG ONLY — do NOT add an
# `ENV CR_PAT=${CR_PAT}` line. ARG makes the value available to the RUN steps
# below (which is all that's needed for the git config trick), while ENV would
# bake the live token into the final image's environment, where it is readable
# by anyone who runs `docker inspect`. See Step 6 for the verification check.
ARG CR_PAT

# Install curl (for health checks) and git (for private GitHub dependencies)
RUN apt-get update && apt-get install -y \
    curl \
    git \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copy requirements and install dependencies
COPY requirements.txt .

# Configure git to use PAT for GitHub access (if private deps)
RUN git config --global url."https://${CR_PAT}@github.com/".insteadOf "https://github.com/" \
    && pip install --no-cache-dir -r requirements.txt \
    && git config --global --unset url."https://${CR_PAT}@github.com/".insteadOf

# Copy application code
COPY . .

# Create non-root user for security
RUN useradd --create-home --shell /bin/bash appuser
RUN chown -R appuser:appuser /app
USER appuser

# Expose the application port
EXPOSE {port}

# Set environment variables
ENV PYTHONPATH=/app
ENV PORT={port}

# Run with gunicorn for production
# Port is read from PORT env var so it can be overridden at runtime
CMD gunicorn --bind 0.0.0.0:$PORT --workers {workers} {module}:{app}
```

**CRITICAL Replacements:**
- `{port}` → Default application port (e.g., 5678). This is the default value for the `PORT` env var — it can be overridden at runtime with `-e PORT=XXXX`
- `{workers}` → Number of workers (e.g., 4, or 1 for background jobs)
- `{module}` → Python module name (e.g., flask_app)
- `{app}` → App variable name (e.g., app or create_app())

**If NO private dependencies**, remove these lines:
```dockerfile
# Remove ARG CR_PAT, git installation, and git config commands
```

Simplified version without private deps:
```dockerfile
FROM python:3.13-slim

# Install curl for health checks
RUN apt-get update && apt-get install -y \
    curl \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

RUN useradd --create-home --shell /bin/bash appuser
RUN chown -R appuser:appuser /app
USER appuser

EXPOSE {port}
ENV PYTHONPATH=/app
ENV PORT={port}

CMD gunicorn --bind 0.0.0.0:$PORT --workers {workers} {module}:{app}
```

## Step 3: Create build-publish.sh Script

Create `build-publish.sh` in the project root:

```bash
#!/bin/sh

# VERSION file path
VERSION_FILE="VERSION"

# Parse command line arguments
NO_CACHE=""
if [ "$1" = "--no-cache" ]; then
    NO_CACHE="--no-cache"
    echo "Building with --no-cache flag"
fi

# Check if VERSION file exists, if not create it with version 1
if [ ! -f "$VERSION_FILE" ]; then
    echo "1" > "$VERSION_FILE"
    echo "Created VERSION file with initial version 1"
fi

# Read current version from file
CURRENT_VERSION=$(cat "$VERSION_FILE" 2>/dev/null)

# Validate that the version is a number
if ! echo "$CURRENT_VERSION" | grep -qE '^[0-9]+$'; then
    echo "Error: Invalid version format in $VERSION_FILE. Expected a number, got: $CURRENT_VERSION"
    exit 1
fi

# Increment version
VERSION=$((CURRENT_VERSION + 1))

echo "Building version $VERSION (incrementing from $CURRENT_VERSION)"

# Build the image with optional --no-cache flag
docker build $NO_CACHE --build-arg CR_PAT=$CR_PAT --platform linux/amd64 -t {registry_url}:$VERSION .

# Tag the same image as latest
docker tag {registry_url}:$VERSION {registry_url}:latest

# Push both tags
docker push {registry_url}:$VERSION
docker push {registry_url}:latest

# Update the VERSION file with the new version
echo "$VERSION" > "$VERSION_FILE"
echo "Updated $VERSION_FILE to version $VERSION"
```

**CRITICAL Replacements:**
- `{registry_url}` → Full container registry URL (e.g., `ghcr.io/{org}/my-flask-app`)

**If NO private dependencies**, remove `--build-arg CR_PAT=$CR_PAT`:
```bash
docker build $NO_CACHE --platform linux/amd64 -t {registry_url}:$VERSION .
```

Make the script executable:
```bash
chmod +x build-publish.sh
```

## Step 4: Create Environment Configuration

### File: `example.env`

Create or update `example.env` with required environment variables for running the containerized application:

```bash
# Server Configuration
PORT={port}

# Database Configuration (if applicable)
{PROJECT_NAME}_DB_HOST=localhost
{PROJECT_NAME}_DB_NAME={project_name}
{PROJECT_NAME}_DB_USER={project_name}
{PROJECT_NAME}_DB_PASSWORD=your_password_here

# Build Configuration (for private dependencies)
CR_PAT=your_github_personal_access_token

# Optional: Additional app-specific variables
DEBUG=False
LOG_LEVEL=INFO
```

**CRITICAL**: Replace:
- `{port}` → Application port (e.g., 5678)
- `{PROJECT_NAME}` → Uppercase project name (e.g., "HYPEROPT_SERVER")
- `{project_name}` → Snake case project name (e.g., "my_flask_app")

**Note:** Remove CR_PAT if you don't have private dependencies.

### Update .gitignore

Add VERSION file and .env to `.gitignore`:

```gitignore
# Environment variables
.env

# Version file (used by build system, not tracked)
VERSION
```

This prevents the VERSION file and environment secrets from being committed.

## Step 5: Create .dockerignore (Optional but Recommended)

Create `.dockerignore` to exclude unnecessary files from Docker build context:

```
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
.venv/
ENV/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Environment files (secrets should not be in image)
.env
*.env
!example.env

# Testing
.pytest_cache/
.coverage
htmlcov/
.tox/

# IDEs
.vscode/
.idea/
*.swp
*.s

Related in Cloud & DevOps