Has anyone managed to successfully deploy a RedwoodJS app onto Fly.io? I’m not having much luck using the default settings provided when running flyctl launch. When I deploy the app is not accessible so the container restarts over and over:
[error] failed to connect to machine: gave up after 15 attempts (in 9.272853195s)
[error] instance refused connection. is your app listening on 0.0.0.0:8910? make sure it is not only listening on 127.0.0.1 (hint: look at your startup logs, servers often print the address they are listening on)
I am also struggling getting deployed to Fly.IO trying to find a platform to start trying the SSR Streaming and serverful deployments.
I tried from a new create-redwood-app but could not figure out the build errors I was getting so I actually just forked the badge app. I stripped it back down to a basic app just trying to have supabase auth with some of the auth pages working with this repo:
The deploy is a little wonky because I have to make sure it does not overwrite the dockerfile as it’ll try to force it on NODE_VERSION 16.13 as well as remove all the other setup.
FYI, I just successfully deployed a fresh Redwood app (V 8.1.1) on Fly.io. I had a little (actually maybe a lot) of help from Cursor. Here are my Cliff notes…
I followed the instructions at Run a RedwoodJS App · Fly Docs. They are sparse: create a new Redwood app, install the Fly command line tool, and run fly launch. The launch command creates the .fly folder contents and a Dockerfile. The .fly/shart.sh and Dockerfile files needed some adjustments.
I ran fly deploy then fly logs. The first few times I had errors, which I copied and pasted into the Cursor chat window. And, after some iteration, I got it working. The notable fine-tuning included…
Update the Node.js version in the Dockerfile to: ARG BASE_IMAGE=node:20-alpine
Update the Yarn version in the Dockerfile. I added the following line: RUN corepack enable && corepack prepare yarn@4.4.0 --activate
Update the Redwood start command in .fly/start.sh. I set it to: yarn rw serve --web-port ${PORT} --web-host 0.0.0.0 --api-port 8911 --api-host 0.0.0.0
And finally, I did a happy dance when it worked.
Here is my entire .fly/start.sh file:
#!/bin/sh
set -ex
if [ -n $MIGRATE_ON_BOOT ]; then
$(dirname $0)/migrate.sh
fi
yarn rw serve --web-port ${PORT} --web-host 0.0.0.0 --api-port 8911 --api-host 0.0.0.0
Here is my entire Dockerfile:
ARG BASE_IMAGE=node:20-alpine
FROM ${BASE_IMAGE} as base
RUN mkdir /app
WORKDIR /app
# Enable Corepack and install the correct Yarn version
RUN corepack enable && corepack prepare yarn@4.4.0 --activate
# Required for building the api and web distributions
ENV NODE_ENV development
FROM base as dependencies
COPY .yarn .yarn
COPY .yarnrc.yml .yarnrc.yml
COPY package.json package.json
COPY web/package.json web/package.json
COPY api/package.json api/package.json
COPY yarn.lock yarn.lock
# Use the correct Yarn version to install dependencies
RUN --mount=type=cache,target=/root/.yarn/berry/cache \
--mount=type=cache,target=/root/.cache yarn install --immutable
COPY redwood.toml .
COPY graphql.config.js .
FROM dependencies as web_build
COPY web web
RUN yarn rw build web
FROM dependencies as api_build
COPY api api
RUN yarn rw build api
FROM dependencies
ENV NODE_ENV production
COPY --from=web_build /app/web/dist /app/web/dist
COPY --from=api_build /app/api /app/api
COPY --from=api_build /app/node_modules/.prisma /app/node_modules/.prisma
COPY .fly .fly
ENTRYPOINT ["sh"]
CMD [".fly/start.sh"]
I get this warning and nothing renders when I deploy your suggestions.
WARNING The app is not listening on the expected address and will not be reachable by fly-proxy.
You can fix this by configuring your app to listen on the following addresses:
- 0.0.0.0:8910
Found these processes inside the machine with open listening sockets:
PROCESS | ADDRESSES
-----------------*---------------------------------------
/.fly/hallpass | [fdaa:5:60a9:a7b:325:e55b:e928:2]:22
I do have this line correctly setup… not sure what’s going on.
In my case, if ever someone is looking for a solution at 2025, here’s my setup:
.fly/migrate.sh
#!/bin/bash
set -ex
npx prisma migrate deploy --schema /home/node/app/api/db/schema.prisma
.fly/release.sh
#!/bin/bash
set -ex
if [ ! -n $MIGRATE_ON_BOOT ]; then
$(dirname $0)/migrate.sh
fi
.fly/start.sh
#!/bin/bash
set -ex
if [ -n $MIGRATE_ON_BOOT ]; then
$(dirname $0)/migrate.sh
fi
yarn rw serve --web-port ${PORT} --web-host 0.0.0.0 --api-port 8911 --api-host 0.0.0.0
.github/workflows/fly-deploy.sh
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/
name: Fly Deploy
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy app
runs-on: ubuntu-latest
concurrency: deploy-group # optional: ensure only one action runs at a time
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- name: Deploy to Fly.io
run: flyctl deploy --remote-only --config fly.toml
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
NODE_ENV: production
Dockerfile
# base
# ----
FROM node:20-bookworm-slim as base
# RUN corepack enable
# We tried to make the Dockerfile as lean as possible. In some cases, that means we excluded a dependency your project needs.
# By far the most common is Python. If you're running into build errors because `python3` isn't available,
# uncomment the line below here and in other stages as necessary:
RUN apt-get update && apt-get install -y \
openssl \
# python3 make gcc \
&& rm -rf /var/lib/apt/lists/*
USER node
WORKDIR /home/node/app
COPY --chown=node:node .yarnrc.yml .
COPY --chown=node:node package.json .
COPY --chown=node:node api/package.json api/
COPY --chown=node:node web/package.json web/
COPY --chown=node:node yarn.lock .
COPY --chown=node:node .yarn/releases/*.cjs .yarn/releases/
# RUN mkdir -p /home/node/.yarn/berry/index
# RUN mkdir -p /home/node/.cache
# RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,uid=1000 \
# --mount=type=cache,target=/home/node/.cache,uid=1000 \
# CI=1 yarn install
RUN yarn install
COPY --chown=node:node redwood.toml .
COPY --chown=node:node graphql.config.js .
COPY --chown=node:node .env.defaults .env.defaults
# api build
# ---------
FROM base as api_build
# If your api side build relies on build-time environment variables,
# specify them here as ARGs. (But don't put secrets in your Dockerfile!)
#
# ARG MY_BUILD_TIME_ENV_VAR
COPY --chown=node:node api api
RUN yarn rw build api
# web prerender build
# -------------------
FROM api_build as web_build_with_prerender
ARG NODE_ENV
ENV NODE_ENV=$NODE_ENV
ARG RUNTIME_ENV
ENV RUNTIME_ENV=$RUNTIME_ENV
ARG WEB_API_URL
ENV WEB_API_URL=$WEB_API_URL
ARG WEB_BUNDLER
ENV WEB_BUNDLER=$WEB_BUNDLER
COPY --chown=node:node web web
RUN yarn rw build web
# web build
# ---------
FROM base as web_build
ARG NODE_ENV
ENV NODE_ENV=$NODE_ENV
ARG RUNTIME_ENV
ENV RUNTIME_ENV=$RUNTIME_ENV
ARG WEB_API_URL
ENV WEB_API_URL=$WEB_API_URL
ARG WEB_BUNDLER
ENV WEB_BUNDLER=$WEB_BUNDLER
COPY --chown=node:node web web
RUN yarn rw build web --no-prerender
# api serve
# ---------
FROM node:20-bookworm-slim as api_serve
# RUN corepack enable
RUN apt-get update && apt-get install -y \
openssl \
# python3 make gcc \
&& rm -rf /var/lib/apt/lists/*
USER node
WORKDIR /home/node/app
COPY --chown=node:node .yarnrc.yml .
COPY --chown=node:node package.json .
COPY --chown=node:node api/package.json api/
COPY --chown=node:node yarn.lock .
COPY --chown=node:node .yarn/releases/*.cjs .yarn/releases/
# RUN mkdir -p /home/node/.yarn/berry/index
# RUN mkdir -p /home/node/.cache
# RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,uid=1000 \
# --mount=type=cache,target=/home/node/.cache,uid=1000 \
# CI=1 yarn workspaces focus api --production
RUN yarn workspaces focus api --production
COPY --chown=node:node redwood.toml .
COPY --chown=node:node graphql.config.js .
COPY --chown=node:node .env.defaults .env.defaults
COPY --chown=node:node --from=api_build /home/node/app/api/dist /home/node/app/api/dist
COPY --chown=node:node --from=api_build /home/node/app/api/db /home/node/app/api/db
COPY --chown=node:node --from=api_build /home/node/app/node_modules/.prisma /home/node/app/node_modules/.prisma
ENV NODE_ENV=production
# default api serve command
# ---------
# If you are using a custom server file, you must use the following
# command to launch your server instead of the default api-server below.
# This is important if you intend to configure GraphQL to use Realtime.
#
# CMD [ "./api/dist/server.js" ]
CMD [ "node_modules/.bin/rw-server", "api" ]
# web serve
# ---------
FROM node:20-bookworm-slim as web_serve
# RUN corepack enable
USER node
WORKDIR /home/node/app
COPY --chown=node:node .yarnrc.yml .
COPY --chown=node:node package.json .
COPY --chown=node:node web/package.json web/
COPY --chown=node:node yarn.lock .
COPY --chown=node:node .yarn/releases/*.cjs .yarn/releases/
# RUN mkdir -p /home/node/.yarn/berry/index
# RUN mkdir -p /home/node/.cache
# RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,uid=1000 \
# --mount=type=cache,target=/home/node/.cache,uid=1000 \
# CI=1 yarn workspaces focus web --production
RUN yarn workspaces focus web --production
COPY --chown=node:node redwood.toml .
COPY --chown=node:node graphql.config.js .
COPY --chown=node:node .env.defaults .env.defaults
COPY --chown=node:node --from=web_build /home/node/app/web/dist /home/node/app/web/dist
ENV NODE_ENV=production \
API_PROXY_TARGET=http://api:8911
# We use the shell form here for variable expansion.
CMD "node_modules/.bin/rw-web-server" "--api-proxy-target" "$API_PROXY_TARGET"
# console
# -------
FROM base as console
# To add more packages:
#
# ```
# USER root
#
# RUN apt-get update && apt-get install -y \
# curl
#
# USER node
# ```
COPY --chown=node:node api api
COPY --chown=node:node web web
COPY --chown=node:node scripts scripts
COPY .fly .fly
COPY --chmod=755 .fly/migrate.sh .fly/migrate.sh
RUN yarn rw build
ENTRYPOINT ["sh"]
CMD [".fly/start.sh"]
fly.toml
# fly.toml app configuration file generated for redwoodjs-app on 2025-05-31T01:09:22+08:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'redwoodjs-app'
primary_region = 'sin'
[build]
[deploy]
release_command = '.fly/release.sh'
[env]
PORT = '8910'
REDWOOD_DISABLE_TELEMETRY = '1'
[http_service]
internal_port = 8910
force_https = true
auto_stop_machines = 'stop'
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[vm]]
memory = '1gb'
cpu_kind = 'shared'
cpus = 1