Building & Deploying a Python Web App

I’ve looked at many of docs/examples plus searched github for example charmcraft.yaml files. Many of them install the app itself from a public github repo or, in one of the examples, a snap. I remain confused on the right/best way to package up a Python web application and deploy it on Juju.

  • Python web applications come in different flavors
    1. No or pyproject.toml file, not intended to be “built”, requirements.txt for dependencies
    2. Can still be “built”, but not intended to be fully pip installable: pip install -e . to get sourcecode dir into virtualenv, requirements.txt like deps
    3. Fully pip installable: requirements in or pyproject.toml, etc.
  • Let’s also assume that our application source code is private, not something publicly accessible. And since it lives right there on our disk, we don’t want to have to get juju authentication to read it from github during the build process. We’d like everything the app needs to run to come right from the same project folder the app is in and be fully uploaded with the rest of the charm.

Given the above, how do we get our application’s source and whatever other file based resources it might need (images, css, etc.) into the charm? Then, how do we get those items to the right place during the install step of the charm?

I’ve seen a few options:

  • Put the app in the src directory and it will be added into the charm.
    • There could be other files I want to include that don’t live in src.
    • I don’t want to re-organize my project to package it into a charm.
    • What I’d really like is for the charm to live at /charm and the app otherwise just look like a Python app.
  • Use parts and the python plugin
    • It seems like this doesn’t work for apps like #1 above?
  • Use parts and dump plugin to include the application’s source code directory
    • There are files being included that don’t need to be

In all the cases I’m not sure how to get the files out of the charm in the install step even once I get them into the charm.


I would full stop NOT recommend you tie your app with your charm logic together… keeping that separation of concerns requires that yes… you git clone, download … perform some sort of action to get your app down into the target environment… but that is sort of the entire point of bundling / packaging things in whatever language you choose.

For myself I use gitlab deploy tokens that are read only, and that takes 90% of the problem away… since i can push bundles in CICD to gitlabs package / container registry… it gives a good practice where in which I am forced to successfully build , deliver to gitlab… in order for it then to be ready for production in deployment via charm

remember you CAN pip install straight off of a git repo too… so if you want to skip the registry step there’s nothing stopping you… but yes you still need to write pyproject.toml files correctly so everything gets bundled up…

We’d like everything the app needs to run to come right from the same project folder the app is in and be fully uploaded with the rest of the charm.

Maybe go deeper on this part… but for me this usually comes in the form of configs and setups that the charm will take care of … I simply set the config at the charm level or with relations to other things like databases

1 Like

Yeah, agreed with ecmp – you can bundle your app’s source code right in with the charm, but it’s generally preferable to keep those concerns separated. You can have the charm download or clone the app’s source code directly, or use a Juju resource (app source code zip). With a resource it’s easy to specify a different version of the app at juju deploy time (or even a different app, depending on how flexible the charm is). With a K8s sidecar charm, you’d use a resource for the container image (OCI/Docker), and the resource can point to a private container registry if you want.

Whether you want the charm source code in the same repo as the app source is up to you. If the charm logic is closely tied to the app anyway, that makes sense. But if the charm is more general than just this app (eg: a Django charm) then it makes sense to have a separate repo. In either case, the question of whether the app source is in the same repo as the charm source is somewhat separate to the issue of what’s bundled into the charm.

Side note: we also have an internal discussion going on at Canonical about “charming 12-factor apps” to make it much easier to deploy standardized apps in various languages/frameworks. However, it’s in the relatively early spec-ing phase, so don’t hold your breath for that.

To answer your question about the mechanics of accessing files bundled into the charm, the environment variable JUJU_CHARM_DIR is set in the context of a hook (or Framework.charm_dir if using the Python Operator Framework). Once again, however, that’s not recommended – a Juju resource is a standard and supported pattern for this. The resource can either be a zip of the source code, or a “pointer” to the source code on some repository / package manager (private or otherwise).

I agree with benhoyt and emcp approach.

Currently my team and I are developing a charm and a snap that creates a Python Web App using FastAPI. The snap is responsible for setting the server while the charm just deals with the application for configuration and to check the health of the service. Unfortunately, the code is still under review, but you can check it out for some inspiration:

Hope this can help you a little bit.

1 Like

Here you might find some inspiration.