From e4c32e3edfdafc005309792be834a264a8c05608 Mon Sep 17 00:00:00 2001 From: Masen Furer <m_github@0x26.net> Date: Tue, 5 Mar 2024 09:28:32 -0800 Subject: [PATCH] Include app.Dockerfile for deploying to container hosting platform (#2784) --- docker-example/Dockerfile | 2 +- docker-example/README.md | 16 +++++++++- docker-example/app.Dockerfile | 57 +++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 docker-example/app.Dockerfile diff --git a/docker-example/Dockerfile b/docker-example/Dockerfile index 36016ecc6..22c8513c0 100644 --- a/docker-example/Dockerfile +++ b/docker-example/Dockerfile @@ -11,7 +11,7 @@ RUN pip install -r requirements.txt # Deploy templates and prepare app RUN reflex init -# Download all npm dependencies and compile and frontend +# Download all npm dependencies and compile frontend RUN reflex export --frontend-only --no-zip # Needed until Reflex properly passes SIGTERM on backend. diff --git a/docker-example/README.md b/docker-example/README.md index 12057df1e..8ac32421d 100644 --- a/docker-example/README.md +++ b/docker-example/README.md @@ -116,4 +116,18 @@ to deploy these services if they are not in active use. ```bash DOMAIN=example.com docker compose -f compose.yaml -f compose.prod.yaml -f compose.tools.yaml up -d -``` \ No newline at end of file +``` + +# Container Hosting + +Most container hosting services automatically terminate TLS and expect the app +to be listening on a single port (typically `$PORT`). + +To host a Reflex app on one of these platforms, like Google Cloud Run, Render, +Railway, etc, use `app.Dockerfile` to build a single image containing a reverse +proxy that will serve that frontend as static files and proxy requests to the +backend for specific endpoints. + +If the chosen platform does not support buildx and thus heredoc, you can copy +the Caddyfile configuration into a separate Caddyfile in the root of the +project. diff --git a/docker-example/app.Dockerfile b/docker-example/app.Dockerfile new file mode 100644 index 000000000..4aae2f31d --- /dev/null +++ b/docker-example/app.Dockerfile @@ -0,0 +1,57 @@ +# This Dockerfile is used to deploy a single-container Reflex app instance +# to services like Render, Railway, Heroku, GCP, and others. + +# It uses a reverse proxy to serve the frontend statically and proxy to backend +# from a single exposed port, expecting TLS termination to be handled at the +# edge by the given platform. +FROM python:3.11 + +# If the service expects a different port, provide it here (f.e Render expects port 10000) +ARG PORT=8080 +# Only set for local/direct access. When TLS is used, the API_URL is assumed to be the same as the frontend. +ARG API_URL +ENV PORT=$PORT API_URL=${API_URL:-http://localhost:$PORT} + +# Install Caddy server inside image +RUN apt-get update -y && apt-get install -y caddy && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Create a simple Caddyfile to serve as reverse proxy +RUN cat > Caddyfile <<EOF +:{\$PORT} + +encode gzip + +@backend_routes path /_event/* /ping /_upload /_upload/* +handle @backend_routes { + reverse_proxy localhost:8000 +} + +root * /srv +route { + try_files {path} {path}/ /404.html + file_server +} +EOF + +# Copy local context to `/app` inside container (see .dockerignore) +COPY . . + +# Install app requirements and reflex in the container +RUN pip install -r requirements.txt + +# Deploy templates and prepare app +RUN reflex init + +# Download all npm dependencies and compile frontend +RUN reflex export --frontend-only --no-zip && mv .web/_static/* /srv/ && rm -rf .web + +# Needed until Reflex properly passes SIGTERM on backend. +STOPSIGNAL SIGKILL + +EXPOSE $PORT + +# Apply migrations before starting the backend. +CMD [ -d alembic ] && reflex db migrate; \ + caddy start && reflex run --env prod --backend-only --loglevel debug