Tailwind in a Django Dockerfile

Introduction

In this post, we will explore how to integrate Tailwind CSS into a Django Dockerfile without requiring Node.js in the final image. This approach is particularly useful when your project does not use any JavaScript frameworks,therefore eliminating the need for Node.js. By adding a Tailwind CSS compile stage to our Dockerfile, we can streamline our setup and maintain a lightweight final image. Let’s dive in and see how it’s done!

Prerequisites

  • A Django project with an existing Dockerfile.
  • Familiarity with Tailwind CSS’s build process.
  • Basic knowledge of Docker and Dockerfile syntax.

Setting Up the Project

  1. Using the CDN: This approach is simple but not suitable for production and high-performance websites due to potential latency and dependency on external resources.

  2. Using the Tailwind CLI: This method is straightforward but may not work well if you have additional dependencies like daisyUI that require Node.js for building.

  3. Using npm (requires Node.js): This is the most robust approach for achieving optimal performance. However, to avoid the overhead and increased Docker image sizes associated with installing Node.js in the final image, we will use a multi-stage Docker build to compile Tailwind CSS without including Node.js in the final image.

Got it. Let’s fill in the sections with the appropriate content and provide the related Dockerfile code.

Dockerfile Configuration

Base Image:

We start by defining the base image for our Python environment. For example python:3.12.7-slim-bookworm image, which is a lightweight and efficient base for our Django application.

FROM docker.io/python:3.12.7-slim-bookworm AS python-base

Node.js Build Stage:

Next, we set up a Node.js build stage to handle the Tailwind CSS compilation. We use the node:23.1-bookworm-slim image for this purpose. In this stage, we copy the necessary frontend files, install dependencies using npm ci, and run the Tailwind CSS build process using npx tailwindcss.

FROM node:23.1-bookworm-slim AS node-build-stage

Installing Dependencies:

In the Node.js build stage, we install the necessary dependencies for Tailwind CSS using npm ci. This ensures that we have a clean and reproducible environment for building our CSS.

COPY ./frontend/package*.json ./
RUN npm ci

Configuring Tailwind CSS:

We then copy the Tailwind CSS configuration files (tailwind.config.js and postcss.config.js) and the input CSS file (input.css) into the Node.js build stage.

COPY ./frontend/tailwind.config.js ./
COPY ./frontend/postcss.config.js ./
COPY ./frontend/input.css ./

Building Tailwind CSS:

We run the Tailwind CSS build process using npx tailwindcss, specifying the input and output files and enabling minification for production use.

RUN npx tailwindcss \
    -i input.css \
    -o ./tailwind.css \
    --minify

Copy the compiled tailwind to django:

FROM python-base AS python-run-stage
#
# Others
#
COPY --from=node-build-stage /src/tailwind.css ${APP_HOME}/path/to/static/compiled/

ENTRYPOINT ["some/entrypoint"]

Final step & Important step:

The final image should be:

FROM docker.io/python:3.12.7-slim-bookworm AS python-base
FROM node:23.1-bookworm-slim AS node-build-stage
WORKDIR /src

# assuming your fronend files are at ./frontend
COPY ./frontend/package*.json ./
RUN npm ci

# these are needed for tailwind build
COPY ./frontend/tailwind.config.js ./
COPY ./frontend/postcss.config.js ./
COPY ./frontend/input.css ./

# Copy the templates for tailwind to work
COPY ./dir/to/templates/ ./templates/
COPY ./dir/to/static/js/ ./js/

# This builds the tailwind's minified version
RUN npx tailwindcss \
    -i input.css \
    -o ./tailwind.css \
    --minify

FROM python-base AS python-run-stage
#
# Others
#
COPY --from=node-build-stage /src/tailwind.css ${APP_HOME}/path/to/static/compiled/

ENTRYPOINT ["some/entrypoint"]

Important step!

You should add this line to your docker compose if you have a volume that mounts your project dir to container. This causes the docker to remove the compiled tailwind as it does not exist in your local files but it does exist inside the docker.

services:
  django:
    .
    .
    .
    volumes:
      - .:/app:z # This mounts your local files inside docker(for live reload)
      - /app/path/to/static/compiled # ignore the compiled tailwind

Conclusion

In this tutorial, we explored how to integrate Tailwind CSS into a Django Dockerfile without requiring Node.js in the final image. By leveraging multi-stage builds, we were able to compile Tailwind CSS in a separate Node.js build stage and then copy the compiled CSS into the final Python image. This approach helps maintain a lightweight and efficient Docker image, streamlining the development workflow and reducing the complexity of managing multiple Docker images. We hope this guide helps you achieve a more efficient setup for your Django projects.

Additional Resources

FAQs

1. Why not use the CDN for Tailwind CSS?

Using the CDN is simple but not suitable for production and high-performance websites due to potential latency and dependency on external resources.

2. Can I use Tailwind CLI without Node.js?

Some Tailwind dependancies require Node.js to run, which is why we use a multi-stage Docker build to compile Tailwind CSS without including Node.js in the final image. But Yes if you just need raw tailwind its better to use CLI version

3. How do I handle additional Tailwind CSS plugins?

You can install additional Tailwind CSS plugins by adding them to your package.json and configuring them in your tailwind.config.js file. Ensure that these steps are included in the Node.js build stage of your Dockerfile.

4. What if I need to update my Tailwind CSS configuration?

If you need to update your Tailwind CSS configuration, you can modify the tailwind.config.js file and rebuild your Docker image to apply the changes.

5. How do I ensure my Docker image remains lightweight?

By using multi-stage builds, you can compile Tailwind CSS in a separate stage and only copy the necessary files into the final image, keeping it lightweight and efficient.

Feel free to adjust the content as needed to better fit your specific project and audience.