HomeBlog

Deploying Reflex Front-End with Caddy

3 min read

Reflex is a versatile framework that allows you to build web applications with both front-end and back-end components. However, there are instances where you may only want a front-end application without the Reflex backend state management. Perhaps your backend is hosted separately, or your application doesn't require server-side logic at all. In this blog post, we'll walk through how to use Reflex to generate front-end code and serve it statically using Caddy, a powerful and easy-to-configure web server, all within a Docker container.

Building the Front-End

To deploy just the front-end of a Reflex application, we need to generate static files that a web server can serve. Reflex makes this straightforward with the reflex export command. By using the --frontend-only flag, Reflex compiles your front-end code into static assets like HTML, CSS, and JavaScript, excluding any backend dependencies. In this setup, we'll leverage a Docker container to automate the process of building these static files. The provided Dockerfile includes a multi-stage build that handles this efficiently.

Setting Up Caddy

Caddy is an excellent choice for serving static files due to its simplicity and robust features. In this deployment, Caddy will act as our web server, delivering the static front-end files generated by Reflex. The configuration for Caddy is defined in a Caddyfile.

Here's the one provided:

:{$PORT}

encode gzip

root * /srv
route {
    try_files {path} {path}/ /404.html
    file_server
}

Let's break down what this does:

  • PORT: Caddy listens on the port specified by the PORT environment variable, making it flexible for different deployment environments.

  • encode gzip: Enables gzip compression to reduce the size of responses, improving load times.

  • root * /srv: Sets the root directory for serving files to /srv, where our static files will reside.

  • route: Defines how Caddy handles requests:

  • try_files {path} {path}/ /404.html: Attempts to serve the requested path directly, then as a directory (e.g., appending /), and falls back to /404.html if neither exists.

  • file_server: Activates Caddy's static file serving capability.

This configuration ensures that your front-end files are served efficiently, with a fallback to a 404 page for unmatched routes.

Dockerfile Explanation

The provided Dockerfile is designed to build and deploy a Reflex front-end application in a single container. It uses a multi-stage build process to keep the final image lightweight. Here's how it works:

Builder Stage

FROM python:3.13 as builder

RUN mkdir -p /app/.web
RUN python -m venv /app/.venv
ENV PATH="/app/.venv/bin:$PATH"

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt

COPY rxconfig.py ./
RUN reflex init

COPY *.web/bun.lockb *.web/package.json .web/
RUN if [ -f .web/bun.lockb ]; then cd .web && ~/.local/share/reflex/bun/bin/bun install --frozen-lockfile; fi

COPY . .

ARG PORT API_URL
RUN API_URL=${API_URL:-http://localhost:$PORT} reflex export --loglevel debug --frontend-only --no-zip && mv .web/_static/* /srv/ && rm -rf .web

Base Image: Starts with python:3.13 to provide a full Python environment for building.

Environment Setup: Creates a virtual environment at /app/.venv and adds it to the PATH.

Dependencies: Installs Python dependencies from requirements.txt and initializes Reflex with reflex init.

Front-End Dependencies: If a bun.lockb file exists, it installs front-end dependencies using Bun, Reflex's preferred package manager.

Project Files: Copies your entire project into /app.

Static Export: Runs reflex export --frontend-only --no-zip to generate static files, placing them in .web/_static. The API_URL is set to a default of http://localhost:$PORT unless overridden. The static files are then moved to /srv.

Final Stage


FROM python:3.13-slim

RUN apt-get update -y && apt-get install -y caddy && rm -rf /var/lib/apt/lists/*

ARG PORT API_URL
ENV PATH="/app/.venv/bin:$PATH" PORT=$PORT API_URL=${API_URL:-http://localhost:$PORT} PYTHONUNBUFFERED=1

WORKDIR /app
COPY --from=builder /app /app
COPY --from=builder /srv /srv

STOPSIGNAL SIGKILL

EXPOSE $PORT

CMD caddy run
  • Base Image: Uses python:3.13-slim for a smaller image size.

  • Caddy Installation: Installs Caddy using apt-get.

  • Environment Variables: Sets PORT and API_URL, with API_URL defaulting to http://localhost:$PORT.

  • File Copy: Copies /app and /srv from the builder stage. (Note: /app may not be strictly necessary since Caddy only serves from /srv, but it's included in the provided setup.)

  • Port Exposure: Exposes the specified $PORT for external access.

  • Run Command: Starts Caddy with caddy run, serving the static files from /srv.

HTTPS: This setup assumes TLS termination is handled by the deployment platform (e.g., GCP, Render, Heroku). If you need Caddy to handle HTTPS locally, you'll need to configure it accordingly, though that's beyond this basic guide.

Conclusion

Deploying a Reflex front-end with Caddy is a streamlined process thanks to Reflex's static export capabilities and Caddy's efficient file serving. Using the provided Dockerfile, you can build your front-end into static files and serve them in a lightweight Docker container, perfect for platforms like GCP, Render, Railway, or Heroku. Just remember to set the API_URL correctly if your app relies on a backend, and tweak the Caddyfile as needed for your application's routing requirements. With this approach, you get a fast, scalable front-end deployment without the overhead of Reflex's backend state management - ideal for static sites or apps with separate backends.

Related Blog Posts

Enjoyed this blog post? Check out these related posts!

Reflex Makes SEO Easier: Automatic robots.txt and sitemap.xml Generation

Reflex Makes SEO Easier: Automatic robots.txt and sitemap.xml Generation

Discover how adding your deploy URL in Reflex automatically generates robots.txt and sitemap.xml for easier SEO.

Read More..

Deploying Reflex Front-End with Caddy in Docker

Deploying Reflex Front-End with Caddy in Docker

A step-by-step guide to building and serving Reflex static front-end files using Caddy in a Docker container

Read More..

Adding middleware to FastAPI Applications: Process Time, Security, and Compression

Adding middleware to FastAPI Applications: Process Time, Security, and Compression

A practical guide to implementing middleware in FastAPI for better performance, security, and efficiency.

Read More..

Simple CI/CD for Your FastAPI App with Google Cloud Build and Cloud Run

Simple CI/CD for Your FastAPI App with Google Cloud Build and Cloud Run

Push code, deploy automatically: A simple CI/CD guide for your web app.

Read More..

Contact Me

Have a project in mind? Send me an email at hello@davidmuraya.com and let's bring your ideas to life. I am always available for exciting discussions.

© 2025 David Muraya. All rights reserved.