SiteHost

Python Container

The Python Container

As anyone familiar with Python is abundantly aware, there are no shortage of package managers available for use. So, given that it wasn't going to be reasonable to bundle them all into a single Cloud Container image, we made a call to pick out some of the more popular options:

  • UV
  • Conda & Mamba
  • Poetry
  • Pyenv

You might have your own favourite that we've omitted. We appreciate everyone's got their own preferences, however we've only included package managers that we have been able to test and get consistent results with. Feel free to let us know if we've missed a good one, though.

We've also made the system interpreter (Python 3.12) a little more cleanly integrated and bundled in the virtualenv package, so if you just want Python up and running, it's there.

Users of the Python + Miniconda image, our first foray into providing a Python CC image, might notice that this is not compatible with your existing image. You'll need to create a new container in order to use the new image.

Creating a Python Container

The Python Container is a service container. In order to create one, simply pick the 'SERVICE IMAGE' tab when creating a container and select the Python container image.

Using your Package Manager

For the most part, using any of the bundled package managers will work exactly as the relevant documentation suggests. There are a few things to keep in mind though:

  • We've set a number of environment variables that control the behaviour of each of these package managers. You'll be able to see what we set them to using the env command while in an SSH session. The important thing to remember is that while you're welcome to change these, they're there to ensure that your environment is persistent in one of the Cloud Container mounts under /container, or in a Container Volume.
  • Some Python packages are reliant on system packages - for example, pymysql expects the system to provide the MySQL client libraries. We've bundled a lot of commonly used packages, but if you find you're missing a dependency you've got a couple options:
    • Consider Conda/Mamba as an option. While they're ostensibly a little more involved than using UV or Poetry, they both allow defining more rounded-out environments, including dependency packages.
    • Get in touch with us! If you think we're missing a useful dependency we'd be happy to hear from you and potentially add it in.

UV

UV is a fast and powerful package manager built by the team at Astral, who are also responsible for the popular Ruff project. UV supports both package management and Python environment management (meaning you can pick the Python version you run on).

UV will use /container/application/.uv as a default management root.

Conda & Mamba

Conda & Mamba are two sides of the same coin. Conda is the original, fully-fledged environment management solution, while Mamba is a faster, nearly feature-complete alternative.

Conda and Mamba will default to using /container/application/.conda as a management root.

They both read from /opt/conda/.condarc for more detailed configuration. Any changes to this file are not guaranteed to persist between container restarts.

Poetry

Poetry is similar to UV, though a little more mature as a packaging solution. While it has experimental Python version management built into the bundled version, it's still not quite complete.

Poetry will by default use /container/application/.poetry for its operations.

Pyenv

Pyenv is less of a package manager and more of an interpreter manager - it allows you to install the Python version you want, allowing you to spin up a virtualenv against that version. Pyenv will install interpreters into /container/application/.pyenv.

System Interpreter

If you'd prefer to use the system interpreter, you'll also need to use virtualenv and pip to manage your packages. Make sure your environment is persisted somewhere in /container/application or on a volume.

Adding a Supervisor Process

More likely than not, you're going to want Supervisor to take control of your process to ensure it's always running. A default template has been provided (commented out in /container/config/supervisor.conf). How this is configured is somewhat dependent on the package manager you're using.

UV & Poetry

Both UV and Poetry are safe to run with their respective CLI tooling - i.e. uv run or poetry run. As long as Supervisor is configured to run in the correct project directory, your app should just run. For example, if you wanted to run a Litestar project that's based in /container/application/uv, you'd use something like this:

[program:uv_app]
user=www-data
directory=/container/application/uv
autostart=true
autorestart=true
command=/bin/bash -c "uv run litestar --app main:app run --host 0.0.0.0 --port 8010"
stdout_logfile=/container/logs/supervisor/%(program_name)s-stdout.log
stderr_logfile=/container/logs/supervisor/%(program_name)s-stderr.log

Mamba & Conda

Neither Mamba nor Conda are quite as well-suited to running in non-interactive environments, which means the conda run and mamba run commands won't play nice with Supervisor. The alternative here is to use a direct path to the binary you want to run. This is usually within your environment - for example, installing the same litestar in a Conda environment, you can validate the path:

(condatest) python@ch-testserv:/container/application/conda$ which litestar
/container/application/.conda/envs/condatest/bin/litestar

You can then use that to run the litestar binary, which will respect the environment it's sourced from. The same works with Python:

(condatest) python@ch-testserv:/container/application/conda$ which python
/container/application/.conda/envs/condatest/bin/python

Those translate to Supervisor configs such as:

[program:conda_app]
user=www-data
directory=/container/application/conda
autostart=true
autorestart=true
command=/bin/bash -c "/container/application/.conda/envs/condatest/bin/litestar --app main:app run --host 0.0.0.0 --port 8012"
stdout_logfile=/container/logs/supervisor/%(program_name)s-stdout.log
stderr_logfile=/container/logs/supervisor/%(program_name)s-stderr.log

Pyenv

Similar to Mamba and Conda, Pyenv doesn't work quite so nicely when running in a non-interactive environment. While it's much more lightweight - in fact just running a glorified virtual environment that references the Python version you want - you'll still need to reference its path explicitly.

[program:pyenv_app]
user=www-data
directory=/container/application/pyenv
autostart=true
autorestart=true
command=/bin/bash -c "/container/application/.pyenv/versions/pyenvtest/bin/litestar --app app:app run --host 0.0.0.0 --port 8014"
stdout_logfile=/container/logs/supervisor/%(program_name)s-stdout.log
stderr_logfile=/container/logs/supervisor/%(program_name)s-stderr.log

System Environment

If you're using the system environment (probably with virtualenv), you'll be able to directly reference the venv path. If this exists within your application directory (e.g. /container/application/.venv) you can reference it as a relative path:

[program:pyapp]
user=www-data
directory=/container/application/
autostart=true
autorestart=true
command=/bin/bash -c ".venv/bin/litestar --app app:app run --host 0.0.0.0"
stdout_logfile=/container/logs/supervisor/%(program_name)s-stdout.log
stderr_logfile=/container/logs/supervisor/%(program_name)s-stderr.log

Cronjobs

Many applications need to run scheduled jobs - updating data, running mailers, etc. While you can incorporate these into Python apps using packages like APScheduler, most Linux systems (our Cloud Container images included) come with Cron as a scheduler which is usually preferred. You can read more about using Cloud Container crontabs in our KB. Similar to using supervisorctl, unless you're using UV/Poetry, commands won't play nice in the non-interactive environment, so you may need to use explicit paths to binaries.

Web Applications

If you're looking to run a Python web application, you've got a couple options:

  • Run it standalone using an ASGI/WSGI solution such as Gunicorn. This will work if you don't need your app to listen on port 80 or 443 (since those are reserved for the server's built-in NGINX proxy container).
  • Run it using our NGINX Proxy container to rewrite requests to the container's listen port. The server's NGINX Proxy will forward requests to your own NGINX Proxy container, which can then be configured to forward on to your Python app however you would like.