OpenParable
Skip to content
← Back to skills

Python Deploy

DevOps & Security
python-deploy

Python Deploy is an Agent Skill for devops & security published in the nixopus/nixopus repository on GitHub: a SKILL.md instruction file an AI agent can load to do this task.

Build and deploy Python applications — uv, poetry, pdm, pipenv, pip, framework detection, and Dockerfile patterns. Use when deploying a Python project, or when requirements.txt, pyproject.toml, or Pipfile is detected.

View on GitHub ↗
QUICK START

How to use this skill

Bring this guide into your coding agent with a prompt tailored to the tool you use.

  1. Open your project in Codex.
  2. Copy and paste the prompt below.
  3. Review files and risks before approving any action.
Prompt to paste
Inspect the Agent Skill at https://github.com/nixopus/nixopus/blob/HEAD/api/skills/python-deploy/SKILL.md and all supporting files. Report provenance, permissions, scripts, dependencies, and security risks. Propose an exact installation into .agents/skills/python-deploy, but wait for my approval before writing files or running scripts.

Copying this prompt does not install or run the skill. Review third-party files before use.

Python Deployment

Detection

Project is Python if any of these exist:

FilePackage manager / style
pyproject.tomlModern (PEP 517/518), uv, poetry, pdm, or pip
requirements.txtpip
setup.pysetuptools
PipfilePipenv

Entry point files (checked in order): app.py, main.py, server.py, run.py, wsgi.py, asgi.py

Versions

Python version priority:

  1. .python-version file or mise.toml / .tool-versions
  2. pyproject.toml → [project].requires-python (e.g. >=3.11)
  3. Pipfile → [requires].python_version
  4. runtime.txt
  5. Defaults to 3.13

Package Managers

Detect in order:

  1. Lock files: poetry.lock → Poetry, pdm.lock → PDM, Pipfile.lock → Pipenv, uv.lock → uv
  2. Manifest: pyproject.toml (poetry/pdm/uv) or Pipfile (pipenv) or requirements.txt (pip)
  3. Default: pip with requirements.txt

Install Commands

ManagerInstall command
pippip install --no-cache-dir -r requirements.txt
Poetrypoetry install --only main --no-interaction
PDMpdm install --prod --no-lock
uvuv sync --frozen --no-dev
Pipenvpipenv install --deploy --system

Runtime Variables

Configure the Python runtime for production: enable the fault handler for crash diagnostics (PYTHONFAULTHANDLER=1), disable output buffering for real-time logs (PYTHONUNBUFFERED=1), randomize hash seeds (PYTHONHASHSEED=random), and skip bytecode generation (PYTHONDONTWRITEBYTECODE=1). Suppress pip version checks with PIP_DISABLE_PIP_VERSION_CHECK=1.

Build & Start

Start Command Resolution

  1. Framework-specific (see below)
  2. pyproject.toml → [project.scripts] or [tool.poetry.scripts]
  3. setup.py → entry_points
  4. manage.py → Django
  5. Root entry files (first found): app.py, main.py, server.py, run.py → python <file>
  6. Conventions: wsgi.py, asgi.py

Most Python web apps have no build step (interpreted).

Port Detection

  1. PORT from .env or .env.example
  2. Framework defaults:
FrameworkDefault port
Flask5000
Django / FastAPI / Starlette / Gunicorn / Uvicorn8000
Streamlit8501
Gradio7860
Default8000

Framework Detection

From dependencies in pyproject.toml, requirements.txt, or Pipfile:

Package patternFrameworkCategory
flaskFlaskBackend
djangoDjangoFullStack
fastapiFastAPIBackend
starletteStarletteBackend
python-fasthtmlFastHTMLBackend
uvicornASGI serverBackend
gunicornWSGI serverBackend
streamlitStreamlitApp
gradioGradioApp
pelicanPelicanStatic
mkdocsMkDocsDocs

Framework-Specific Start Commands

Flask — gunicorn <module>:app -b 0.0.0.0:${PORT:-5000} if gunicorn is a dependency, else flask run Django — python manage.py migrate && gunicorn <wsgi_module>:application -b 0.0.0.0:${PORT:-8000} --workers 2. Detect app from WSGI_APPLICATION in settings. FastAPI — uvicorn <module>:app --host 0.0.0.0 --port ${PORT:-8000} --workers 1 FastHTML — uvicorn <module>:app --host 0.0.0.0 --port ${PORT:-8000} Streamlit — streamlit run <entry>.py --server.port ${PORT:-8501} --server.address 0.0.0.0

System Dependencies

PackageBuild-timeRuntime
psycopg2libpq-dev, gcclibpq5
mysqlclientdefault-libmysqlclient-dev, gcclibmariadb3
pillowlibjpeg-dev, zlib1g-dev, libfreetype6-dev—
cryptographylibssl-dev, libffi-dev—
pycairolibcairo2-dev, pkg-configlibcairo2
lxmllibxml2-dev, libxslt1-devlibxml2, libxslt1.1
pycurllibcurl4-openssl-dev—
pdf2image—poppler-utils
pydub—ffmpeg
playwright—Chromium headless shell

Install Stage Optimization

Copy pyproject.toml + lockfile first for layer caching, then full source.

Caching

Leverage BuildKit cache mounts to speed up rebuilds:

  • pip: RUN --mount=type=cache,target=/root/.cache/pip pip install -r requirements.txt
  • uv: RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen

Environment Variable Semantics

PatternLikely dependency
DATABASE_URLPostgreSQL, MySQL
REDIS_URLRedis
SECRET_KEYDjango, Flask
DJANGO_SETTINGS_MODULEDjango
AWS_* / S3_*AWS

Dockerfile Patterns

uv (with lockfile)

FROM python:3.13-slim AS base

FROM base AS deps
WORKDIR /app
COPY pyproject.toml uv.lock ./
RUN pip install uv && uv sync --frozen

FROM base AS runtime
WORKDIR /app
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
COPY --from=deps /app/.venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

pip + requirements.txt

FROM python:3.13-slim AS base

FROM base AS deps
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

FROM base AS runtime
WORKDIR /app
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
COPY --from=deps /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY . .
EXPOSE 8000
CMD ["gunicorn", "wsgi:app", "-b", "0.0.0.0:8000"]

Django with collectstatic

FROM python:3.13-slim AS base

FROM base AS deps
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

FROM base AS build
WORKDIR /app
COPY --from=deps /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY . .
ENV DJANGO_SETTINGS_MODULE=project.settings
RUN python manage.py collectstatic --noinput

FROM base AS runtime
WORKDIR /app
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
COPY --from=build /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY --from=build /app/static /app/static
COPY --from=build /app /app
EXPOSE 8000
CMD ["gunicorn", "project.wsgi:application", "-b", "0.0.0.0:8000"]

Gotchas

  • psycopg2 requires libpq-dev and gcc at build time — use psycopg2-binary instead to avoid native compilation
  • uv --frozen fails if uv.lock doesn't exist — use uv sync without --frozen when no lockfile is present
  • Django collectstatic needs DJANGO_SETTINGS_MODULE and may require a dummy SECRET_KEY at build time
  • Poetry creates virtual environments outside the project by default — set POETRY_VIRTUALENVS_IN_PROJECT=true for Docker builds
  • FastAPI/Uvicorn must bind to 0.0.0.0, not 127.0.0.1, to be reachable from outside the container
  • Python site-packages path includes the minor version (e.g. python3.13) — the COPY path in multi-stage Dockerfiles must match the exact Python version