HomeBlog

Optimizing Reflex Backend Service Performance on Google Cloud

3 min read

David Muraya Blog Header Image

Introduction

Reflex is a Python framework for building web apps, and I recently set out to determine the most efficient way to run a Reflex backend service on Google Cloud Run.

Reflex's backend service is a vital part of the Reflex framework, designed to manage server-side logic for full-stack web applications written entirely in Python. It handles API requests, maintains application state, and ensures smooth communication with data sources or external services. Built on FastAPI, it integrates seamlessly with Reflex's reactive frontend, supporting asynchronous operations for real-time functionality. The service also offers built-in tools for authentication and database integration, simplifying development by abstracting backend complexities and letting developers focus on creating features.

I tested several execution commands to compare their performance, and here's what I discovered.

Methods Tested

1. Gunicorn with Uvicorn Workers

I used the following command:

exec gunicorn --bind :8001 --workers 1 --worker-class uvicorn.workers.UvicornWorker --threads 16 app.app:app

This approach stood out as the best performer. It's reliable, fast, and well-suited for running Reflex apps on Google Cloud.

2. Uvicorn Alone

I tried this command:

exec uvicorn app.app:app --factory --port 8001 --loop uvloop --http httptools

Unfortunately, Uvicorn on its own was too slow on Google Cloud. If you're considering this method, I'd suggest adding uvloop and httptools to your requirements.txt to boost performance slightly, but it still doesn't compete with Gunicorn.

3. Granian

Granian, an ASGI server built with Rust, seemed promising. I tested it with:

exec granian --host 0.0.0.0 --port 8001 --interface asgi --workers 1 --runtime-threads 16 app.app:app

However, it didn't work for me - likely due to compatibility issues with Reflex 0.7.4 or my specific setup.

Reflex has begun migrating to Granian as the ASGI server that ships by default with the framework. You can test this integration by setting the environment variable REFLEX_USE_GRANIAN=1. While there may be some bugs in the current implementation, the Reflex team is actively working to resolve them. The plan is to fully deprecate Uvicorn and transition primarily to Granian by version 0.8.0.

4. Reflex's Built-in Command

I also ran the app using Reflex's default command:

exec reflex run --backend-only

This was noticeably slow on Google Cloud, making it less ideal for production use.

Conclusion

After testing these options, Gunicorn with Uvicorn workers emerged as the clear winner for running a Reflex 0.7.4 app on Google Cloud. It provided the best balance of speed and stability. If you're deploying a Reflex app on Google Cloud, I highly recommend starting with this approach.

Update: Better Results with a Revised Startup Sequence (03 April 2025)

After further experimentation, I've refined my deployment setup by ensuring that the backend is fully operational before serving the frontend. This change has resulted in better overall performance and reliability.

What Changed?

I updated my Dockerfile and introduced a custom start.sh script. The key modifications are replacing my start up command with a custom script:

Dockerfile Changes:

# ..rest of file

RUN chmod +x /app/start.sh
ENTRYPOINT ["/app/start.sh"]

These lines ensure that the startup script has the proper execution permissions and is used as the entry point for the container.

New start.sh Script

#!/bin/sh

# Start Gunicorn in the background
gunicorn --bind :8001 --workers 1 --worker-class uvicorn.workers.UvicornWorker --threads 4 -t 3600 --max-requests 1000 --max-requests-jitter 50 app.app:app &

# Wait for Gunicorn to be ready (example with a ping check)
until curl -s http://127.0.0.1:8001/ping > /dev/null; do
    echo "Waiting for Gunicorn..."
    sleep 1
done

# Start Caddy in the foreground
exec caddy run

In this script, Gunicorn (which runs the Reflex backend service) is started in the background. A loop then checks the /ping endpoint until it confirms that the backend is ready. Only once Gunicorn is responding successfully does the script proceed to launch Caddy, which serves the frontend in the foreground.

Why This Approach Works Better

  • Ensured Readiness: By waiting for the /ping endpoint to return a response (it returns "pong" when successful), we guarantee that the backend is fully up and running before Caddy starts routing requests.

  • Reliable Service Initialization: Running Caddy in the foreground (via exec caddy run) ensures that it becomes the container’s main process, while Gunicorn, having been started first, is already available to handle API requests.

  • Improved User Experience: With the backend confirmed to be active before the frontend is served, users are less likely to encounter errors or delays when the frontend starts making API calls.

This updated startup sequence provides a more robust solution for deploying Reflex on Google Cloud Run, ensuring a smoother and more reliable experience for end users.

Related Blog Posts

Enjoyed this blog post? Check out these related posts!

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..

Optimizing Reflex Performance on Google Cloud Run

Optimizing Reflex Performance on Google Cloud Run

A Comparison of Gunicorn, Uvicorn, and Granian for Running Reflex Apps

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..

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..

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.