There are two ways to think about this.
One is to not use a single fly “app” - what fly calls an app
is often more like a component of a full application. You’d then have one app
for your REST backend, which would likely resemble your current Dockerfile - spin up Python, install dependencies, prepare any assets, serve the API at an endpoint.
You’d separately write your Angular client. The build stage for this would output, effectively, a pile of static HTML/CSS/JS, that you would serve as a second app - either served directly off Tigris as static files, or via a second Dockerfile describing lightweight HTML server. You’d setup environment variables to make it easier for clients to automatically point at server URLs.
You could keep this in the same repo if you want - although I’d argue that once you decouple client/server into different stacks, they are now separate projects - but you’d have to have separate directories, Dockerfiles, and fly.toml
files for each.
There is an alternative, which is you describe a single Dockerfile that could run client or server, depending on what command you run, and you specify that with process groups. I don’t think this makes sense if the two applications are different stacks (eg: Python backend, static HTML frontend); its real use case is, say, using one process to run the full Django stack, and another to just run background tasks via workers (still in your main Python code).
I think the contradiction that’s tripping things up is trying to decouple your application, and then have a single contain “for simplicity”.
You already have a setup that is simple: a single, full-stack application, handling client and server. Decoupling front and backend adds complexity; given that, trying to server two very different stacks from a single container image doesn’t, actually, keep things simple - it makes things harder, as this image is now trying to server two purposes, not one.
Follow the grain of the wood: having two container images (Dockerfiles and toml configs), two apps, and letting them talk to each other, reflects the complexity of the new two-app solution, whilst keeping it as simple as possible.
Things that shape my opinions: I’m a big fan of monoliths and end-to-end full stack development. I also do enjoy more modern, isomorphic-shape development (huge fan of Sveltekit), but it’s inevitably more complex. There’s nothing wrong with Django/Rails/Phoenix to do everything, esp in the era of modern HATEOAS shaped libraries (that can give you some very slick UI without decoupling everything - htmx, Turbo/Stimulus, Phoenix Liveview, etc).