1 min read

Backend + Frontend on a Single Container

If you're like me and you don't want things to be complicated, then you obviously want everything to run in the same container (I'm exagerating obviously)

In this example I'll be containerizing a Django application and a Nuxt.JS application into a single Docker Image. Currently my application only have a few users and it isn't worth the time to over-compilcate things, I want to code, push and let the image update everything together.

This will also simplify the development, versioning and deployment flow.

So let's get right into it.

Folder Structure

I'm using a simple folder structure with `backend/` and `frontend/` where the code resides, there's a bunch of other folders we can ignore (workflows, scripts, configs, docs, etc)

At the root level I have the `Dockerfile`


The Dockerfile contains a frontend-build stage and a backend stage (the one that's deployed), the frontend-build stage uses node:lts and the backend uses some minimal image (here I chose opensuse/leap)

The Dockerfile:

FROM node:lts-bookworm as frontend-build

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
COPY frontend/package*.json ./
COPY frontend/yarn.lock ./

RUN yarn install

# Bundle app source
COPY ./frontend .

RUN yarn generate

FROM opensuse/leap:15.5

RUN zypper --non-interactive si -d python311 python311-pip \
    && zypper --non-interactive in python311 \
    && python3.11 -m ensurepip

COPY backend/requirements.txt /requirements.txt

RUN python3.11 -m pip install -r /requirements.txt


COPY backend .


COPY --from=frontend-build /usr/src/app/.output/public /app/staticfiles

RUN python3.11 manage.py collectstatic --noinput

CMD ["gunicorn", "--bind", "", "accounting.wsgi"]

That's it.

When I push code from any of backend/ or frontend/, my CI/CD will build, bump version and deploy the latest docker image. I've been delaying an article on that kind of (basic) CI/CD (that meets most needs), but if enough interest don't hesitate leaving a comment

Also, SUBSCRIBE! Thank you