============ Docker image ============ Kopf provides pre-built Docker images on the GitHub Container Registry (GHCR) with all extras pre-installed. These images are intended for quick experimentation and ad-hoc operator development. For production, it is recommended to build your own image with ``pip install kopf`` (see :doc:`deployment`). The images are available at: .. code-block:: text ghcr.io/nolar/kopf Each image includes the ``kopf`` CLI, the ``full-auth`` extra (``pykube-ng`` and ``kubernetes`` client libraries), ``uvloop`` for better async performance, and the ``dev`` extra (``oscrypto``, ``certbuilder``, ``certvalidator``, ``pyngrok``) for development convenience. Image variants ============== Images are published for each release in several variants: * **slim** (default) --- based on Debian, larger but with broader compatibility. * **alpine** --- based on Alpine Linux, smaller but may have issues with some native dependencies. Both variants are built for ``linux/amd64`` and ``linux/arm64`` platforms. Image tags ========== For a release such as ``1.43.0`` built with Python 3.14 (the default), the following tags are available: * ``ghcr.io/nolar/kopf:latest`` --- the latest release with the default Python version and the default variant (slim). * ``ghcr.io/nolar/kopf:1.43.0`` --- a specific patch release. * ``ghcr.io/nolar/kopf:1.43`` --- the latest patch within a minor release. * ``ghcr.io/nolar/kopf:v1`` --- the latest release within a major version. To pin a specific Python version or variant, use the extended tag format: * ``ghcr.io/nolar/kopf:1.43.0-python3.14-alpine`` * ``ghcr.io/nolar/kopf:1.43-python3.14-slim`` * ``ghcr.io/nolar/kopf:v1-python3.14`` Tags without a variant suffix (e.g. ``1.43-python3.14``) point to the slim variant. Tags without a Python version (e.g. ``1.43.0``) point to the default Python version. Kubeconfig security =================== Mounting ``~/.kube/config`` directly into a container is risky: the default kubeconfig often contains access tokens or client certificates for multiple clusters, contexts, and service accounts. A container only needs access to one cluster in one context. For safer local development, create a minified copy that contains only the current context: .. code-block:: bash kubectl config view --minify --flatten > dev.kubeconfig Review the resulting file to ensure it contains only what you expect. All examples in this guide mount this minified config rather than the full ``~/.kube/config``. Remember to regenerate the file when switching contexts or after token rotation. Quick start =========== The simplest way to run an operator is to mount a single Python file at ``/app/main.py`` inside the container. The entrypoint will auto-detect it and run ``kopf run -v /app/main.py``: .. code-block:: bash kubectl config view --minify --flatten > dev.kubeconfig docker run --rm -it \ -v ./handler.py:/app/main.py:ro \ -v ./dev.kubeconfig:/root/.kube/config:ro \ ghcr.io/nolar/kopf The ``dev.kubeconfig`` mount gives the operator access to the Kubernetes cluster (see `Kubeconfig security`_ above for how to create it). Running a specific file ======================= To run an operator from a custom path, pass the ``run`` command with the path explicitly: .. code-block:: bash kubectl config view --minify --flatten > dev.kubeconfig docker run --rm -it \ -v ./src:/src:ro \ -v ./dev.kubeconfig:/root/.kube/config:ro \ ghcr.io/nolar/kopf run -v /src/handler.py Extra dependencies ================== If the operator needs additional Python packages, the entrypoint supports two mechanisms for automatic installation at startup. requirements.txt ---------------- Mount a ``requirements.txt`` file at ``/app/requirements.txt``. The entrypoint will run ``pip install -r /app/requirements.txt`` before starting the operator: .. code-block:: bash kubectl config view --minify --flatten > dev.kubeconfig docker run --rm -it \ -v ./handler.py:/app/main.py:ro \ -v ./requirements.txt:/app/requirements.txt:ro \ -v ./dev.kubeconfig:/root/.kube/config:ro \ ghcr.io/nolar/kopf pyproject.toml -------------- Mount the entire project directory at ``/app/``. If a ``pyproject.toml`` is found, the entrypoint will run ``pip install -e /app/`` to install the project and its dependencies (i.e., as an editable project from the source directory): .. code-block:: bash kubectl config view --minify --flatten > dev.kubeconfig docker run --rm -it \ -v .:/app:rw \ -v ./dev.kubeconfig:/root/.kube/config:ro \ ghcr.io/nolar/kopf The main script should be ``main.py`` for auto-start. If not ``main.py``, add the CLI arguments to run the custom modules or files: .. code-block:: bash kubectl config view --minify --flatten > dev.kubeconfig docker run --rm -it \ -v .:/app:rw \ -v ./dev.kubeconfig:/root/.kube/config:ro \ ghcr.io/nolar/kopf run -m myoperator.main .. note:: Note the ``:rw`` (read-write) mode on the ``/app`` directory --- ``pip`` needs it for building the package locally before installing it. Passing CLI options =================== Any arguments passed to the container are forwarded directly to the ``kopf`` CLI. For example, to run in verbose mode with a specific namespace: .. code-block:: bash kubectl config view --minify --flatten > dev.kubeconfig docker run --rm -it \ -v ./handler.py:/app/main.py:ro \ -v ./dev.kubeconfig:/root/.kube/config:ro \ ghcr.io/nolar/kopf run /app/main.py --verbose --namespace=default To see all available CLI options: .. code-block:: bash docker run --rm ghcr.io/nolar/kopf run --help .. seealso:: :doc:`cli` for the full list of command-line options. Local clusters ============== When the Kubernetes API server runs on the host machine (e.g. k3d, k3s, kind, minikube, or Docker Desktop Kubernetes), the container cannot reach it at ``127.0.0.1`` because that address refers to the container itself, not the host. The simplest solution is to use host networking, which shares the host's network stack with the container: .. code-block:: bash kubectl config view --minify --flatten > dev.kubeconfig docker run --rm -it --network host \ -v ./handler.py:/app/main.py:ro \ -v ./dev.kubeconfig:/root/.kube/config:ro \ ghcr.io/nolar/kopf With ``--network host``, the kubeconfig's ``127.0.0.1`` addresses work as-is. This mode is supported on Linux natively, and on macOS via Docker Desktop and OrbStack. If host networking is not an option, rewrite the server address in the kubeconfig to use ``host.docker.internal`` --- a special hostname that resolves to the host machine in Docker Desktop and OrbStack: .. code-block:: bash kubectl config view --minify --flatten | \ sed 's|127\.0\.0\.1|host.docker.internal|' > dev.kubeconfig docker run --rm -it \ -v ./handler.py:/app/main.py:ro \ -v ./dev.kubeconfig:/root/.kube/config:ro \ ghcr.io/nolar/kopf On Linux without Docker Desktop, add ``--add-host host.docker.internal:host-gateway`` to make the hostname resolve to the host. Using with Docker Compose ========================= The image can be used in a Docker Compose setup for local development. Prepare the kubeconfig first: .. code-block:: bash kubectl config view --minify --flatten > dev.kubeconfig .. code-block:: yaml services: operator: image: ghcr.io/nolar/kopf volumes: - ./handler.py:/app/main.py:ro - ./dev.kubeconfig:/root/.kube/config:ro To target a local cluster (k3d, kind, etc.), use host networking: .. code-block:: bash kubectl config view --minify --flatten > dev.kubeconfig .. code-block:: yaml services: operator: image: ghcr.io/nolar/kopf network_mode: host volumes: - ./handler.py:/app/main.py:ro - ./dev.kubeconfig:/root/.kube/config:ro Building your own image ======================= For production deployments, it is recommended to build a custom image rather than relying on the pre-built one. This avoids startup-time dependency installation, ensures reproducible builds, and keeps the image minimal: .. code-block:: dockerfile FROM python:3.14 RUN pip install kopf COPY handler.py /src/handler.py CMD ["kopf", "run", "/src/handler.py", "--verbose"] .. seealso:: :doc:`deployment` for the full deployment guide, including RBAC and Kubernetes Deployment manifests.