Deploy to Fly.io

Guide to deploy the Remix SaaS boilerplate to Fly.io

This guide was shared by a community member to deploy the Remix Supabase Turbo Kit to Fly.io.

First, create the fly.toml in the root directory (configure fly server to your needs):

app = "remix-turbo-kit" primary_region = "lax" kill_signal = "SIGINT" kill_timeout = 5 [build] dockerfile = "Dockerfile" [env] NODE_ENV = "production" [experimental] allowed_public_ports = [] auto_rollback = true [[services]] http_checks = [] internal_port = 3000 processes = ["app"] protocol = "tcp" script_checks = [] [services.concurrency] hard_limit = 25 soft_limit = 20 [[services.ports]] force_https = true handlers = ["http"] port = 80 [[services.ports]] handlers = ["tls", "http"] port = 443 [[services.tcp_checks]] grace_period = "1s" interval = "15s" restart_limit = 0 timeout = "2s"

then we add this dockerfile to your root:

# syntax=docker/dockerfile:1 # Stage 1: Builder ARG NODE_VERSION=20.10.0 FROM node:${NODE_VERSION}-slim AS builder # Set the working directory in the container WORKDIR /app # Install packages needed to build node modules RUN apt-get update -qq && \ apt-get install -y python-is-python3 pkg-config build-essential # Install pnpm RUN npm install -g pnpm # Copy workspace files COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ # Install dependencies for the entire workspace RUN pnpm install # Change working directory to web app WORKDIR /app/apps/web # Copy all files COPY . . # Install dependencies for the web app RUN pnpm install # Define build arguments ARG NODE_ENV ARG VITE_SITE_URL ARG VITE_PRODUCT_NAME ARG VITE_SITE_TITLE ARG VITE_SITE_DESCRIPTION ARG VITE_DEFAULT_THEME_MODE ARG VITE_DEFAULT_LOCALE ARG VITE_AUTH_PASSWORD ARG VITE_AUTH_MAGIC_LINK ARG CONTACT_EMAIL ARG VITE_ENABLE_THEME_TOGGLE ARG VITE_ENABLE_PERSONAL_ACCOUNT_DELETION ARG VITE_ENABLE_PERSONAL_ACCOUNT_BILLING ARG VITE_ENABLE_TEAM_ACCOUNTS ARG VITE_ENABLE_TEAM_ACCOUNT_DELETION ARG VITE_ENABLE_TEAM_ACCOUNTS_BILLING ARG VITE_ENABLE_TEAM_ACCOUNTS_CREATION ARG VITE_ENABLE_NOTIFICATIONS ARG VITE_REALTIME_NOTIFICATIONS ARG VITE_SUPABASE_URL ARG VITE_SUPABASE_ANON_KEY ARG SUPABASE_SERVICE_ROLE_KEY ARG VITE_BILLING_PROVIDER ARG VITE_STRIPE_PUBLISHABLE_KEY ARG STRIPE_SECRET_KEY ARG STRIPE_WEBHOOK_SECRET ARG VITE_CMS_CLIENT ARG VITE_KEYSTATIC_CONTENT_PATH ARG VITE_LOCALES_PATH ARG MAILER_PROVIDER ARG EMAIL_SENDER ARG EMAIL_HOST ARG EMAIL_PORT ARG EMAIL_USER ARG EMAIL_PASSWORD ARG EMAIL_TLS ARG GADGET_SECRET_KEY ARG VITE_TEAM_NAVIGATION_STYLE ARG VITE_USER_NAVIGATION_STYLE ARG VITE_THEME_COLOR ARG VITE_THEME_COLOR_DARK ARG SIGN_IN_PATH ARG SIGN_UP_PATH ARG TEAM_ACCOUNTS_HOME_PATH ARG INVITATION_PAGE_PATH ARG VITE_DISPLAY_TERMS_AND_CONDITIONS_CHECKBOX # Set environment variables ENV NODE_ENV=${NODE_ENV} ENV VITE_SITE_URL=${VITE_SITE_URL} ENV VITE_PRODUCT_NAME=${VITE_PRODUCT_NAME} ENV VITE_SITE_TITLE=${VITE_SITE_TITLE} ENV VITE_SITE_DESCRIPTION=${VITE_SITE_DESCRIPTION} ENV VITE_DEFAULT_THEME_MODE=${VITE_DEFAULT_THEME_MODE} ENV VITE_DEFAULT_LOCALE=${VITE_DEFAULT_LOCALE} ENV VITE_AUTH_PASSWORD=${VITE_AUTH_PASSWORD} ENV VITE_AUTH_MAGIC_LINK=${VITE_AUTH_MAGIC_LINK} ENV CONTACT_EMAIL=${CONTACT_EMAIL} ENV VITE_ENABLE_THEME_TOGGLE=${VITE_ENABLE_THEME_TOGGLE} ENV VITE_ENABLE_PERSONAL_ACCOUNT_DELETION=${VITE_ENABLE_PERSONAL_ACCOUNT_DELETION} ENV VITE_ENABLE_PERSONAL_ACCOUNT_BILLING=${VITE_ENABLE_PERSONAL_ACCOUNT_BILLING} ENV VITE_ENABLE_TEAM_ACCOUNTS=${VITE_ENABLE_TEAM_ACCOUNTS} ENV VITE_ENABLE_TEAM_ACCOUNT_DELETION=${VITE_ENABLE_TEAM_ACCOUNT_DELETION} ENV VITE_ENABLE_TEAM_ACCOUNTS_BILLING=${VITE_ENABLE_TEAM_ACCOUNTS_BILLING} ENV VITE_ENABLE_TEAM_ACCOUNTS_CREATION=${VITE_ENABLE_TEAM_ACCOUNTS_CREATION} ENV VITE_ENABLE_NOTIFICATIONS=${VITE_ENABLE_NOTIFICATIONS} ENV VITE_REALTIME_NOTIFICATIONS=${VITE_REALTIME_NOTIFICATIONS} ENV VITE_SUPABASE_URL=${VITE_SUPABASE_URL} ENV VITE_SUPABASE_ANON_KEY=${VITE_SUPABASE_ANON_KEY} ENV SUPABASE_SERVICE_ROLE_KEY=${SUPABASE_SERVICE_ROLE_KEY} ENV VITE_BILLING_PROVIDER=${VITE_BILLING_PROVIDER} ENV VITE_STRIPE_PUBLISHABLE_KEY=${VITE_STRIPE_PUBLISHABLE_KEY} ENV STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY} ENV STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET} ENV VITE_CMS_CLIENT=${VITE_CMS_CLIENT} ENV VITE_KEYSTATIC_CONTENT_PATH=${VITE_KEYSTATIC_CONTENT_PATH} ENV VITE_LOCALES_PATH=${VITE_LOCALES_PATH} ENV MAILER_PROVIDER=${MAILER_PROVIDER} ENV EMAIL_SENDER=${EMAIL_SENDER} ENV EMAIL_HOST=${EMAIL_HOST} ENV EMAIL_PORT=${EMAIL_PORT} ENV EMAIL_USER=${EMAIL_USER} ENV EMAIL_PASSWORD=${EMAIL_PASSWORD} ENV EMAIL_TLS=${EMAIL_TLS} ENV GADGET_SECRET_KEY=${GADGET_SECRET_KEY} ENV VITE_TEAM_NAVIGATION_STYLE=${VITE_TEAM_NAVIGATION_STYLE} ENV VITE_USER_NAVIGATION_STYLE=${VITE_USER_NAVIGATION_STYLE} ENV VITE_THEME_COLOR=${VITE_THEME_COLOR} ENV VITE_THEME_COLOR_DARK=${VITE_THEME_COLOR_DARK} ENV SIGN_IN_PATH=${SIGN_IN_PATH} ENV SIGN_UP_PATH=${SIGN_UP_PATH} ENV TEAM_ACCOUNTS_HOME_PATH=${TEAM_ACCOUNTS_HOME_PATH} ENV INVITATION_PAGE_PATH=${INVITATION_PAGE_PATH} ENV VITE_DISPLAY_TERMS_AND_CONDITIONS_CHECKBOX=${VITE_DISPLAY_TERMS_AND_CONDITIONS_CHECKBOX} # Build the web app RUN pnpm run build # Stage 2: Runner FROM node:${NODE_VERSION}-slim AS runner # Set the working directory in the container WORKDIR /app # Copy built application from the builder stage COPY --from=builder /app/apps/web /app # Install pnpm in the final stage to ensure it's available for runtime RUN npm install -g pnpm RUN pnpm install --prod # Expose the port the app runs on EXPOSE 8080 # Start the server by default, this can be overwritten at runtime CMD ["pnpm", "start"]

Lastly, add this workflow file for the deployment:

name: Fly Deploy on: push: branches: - main jobs: deploy: name: Deploy app runs-on: ubuntu-latest concurrency: deploy-group environment: production steps: - uses: actions/checkout@v4 - uses: superfly/flyctl-actions/setup-flyctl@master - run: | flyctl deploy --remote-only \ --build-arg NODE_ENV="${{ vars.NODE_ENV }}" \ --build-arg VITE_SITE_URL="${{ vars.VITE_SITE_URL }}" \ --build-arg VITE_PRODUCT_NAME="${{ vars.VITE_PRODUCT_NAME }}" \ --build-arg VITE_SITE_TITLE="${{ vars.VITE_SITE_TITLE }}" \ --build-arg VITE_SITE_DESCRIPTION="${{ vars.VITE_SITE_DESCRIPTION }}" \ --build-arg VITE_DEFAULT_THEME_MODE="${{ vars.VITE_DEFAULT_THEME_MODE }}" \ --build-arg VITE_DEFAULT_LOCALE="${{ vars.VITE_DEFAULT_LOCALE }}" \ --build-arg VITE_AUTH_PASSWORD="${{ vars.VITE_AUTH_PASSWORD }}" \ --build-arg VITE_AUTH_MAGIC_LINK="${{ vars.VITE_AUTH_MAGIC_LINK }}" \ --build-arg CONTACT_EMAIL="${{ secrets.CONTACT_EMAIL }}" \ --build-arg VITE_ENABLE_THEME_TOGGLE="${{ vars.VITE_ENABLE_THEME_TOGGLE }}" \ --build-arg VITE_ENABLE_PERSONAL_ACCOUNT_DELETION="${{ vars.VITE_ENABLE_PERSONAL_ACCOUNT_DELETION }}" \ --build-arg VITE_ENABLE_PERSONAL_ACCOUNT_BILLING="${{ vars.VITE_ENABLE_PERSONAL_ACCOUNT_BILLING }}" \ --build-arg VITE_ENABLE_TEAM_ACCOUNTS="${{ vars.VITE_ENABLE_TEAM_ACCOUNTS }}" \ --build-arg VITE_ENABLE_TEAM_ACCOUNT_DELETION="${{ vars.VITE_ENABLE_TEAM_ACCOUNT_DELETION }}" \ --build-arg VITE_ENABLE_TEAM_ACCOUNTS_BILLING="${{ vars.VITE_ENABLE_TEAM_ACCOUNTS_BILLING }}" \ --build-arg VITE_ENABLE_TEAM_ACCOUNTS_CREATION="${{ vars.VITE_ENABLE_TEAM_ACCOUNTS_CREATION }}" \ --build-arg VITE_ENABLE_NOTIFICATIONS="${{ vars.VITE_ENABLE_NOTIFICATIONS }}" \ --build-arg VITE_REALTIME_NOTIFICATIONS="${{ vars.VITE_REALTIME_NOTIFICATIONS }}" \ --build-arg VITE_SUPABASE_URL="${{ vars.VITE_SUPABASE_URL }}" \ --build-arg VITE_SUPABASE_ANON_KEY="${{ vars.VITE_SUPABASE_ANON_KEY }}" \ --build-arg SUPABASE_SERVICE_ROLE_KEY="${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}" \ --build-arg VITE_BILLING_PROVIDER="${{ vars.VITE_BILLING_PROVIDER }}" \ --build-arg VITE_STRIPE_PUBLISHABLE_KEY="${{ vars.VITE_STRIPE_PUBLISHABLE_KEY }}" \ --build-arg STRIPE_SECRET_KEY="${{ secrets.STRIPE_SECRET_KEY }}" \ --build-arg STRIPE_WEBHOOK_SECRET="${{ secrets.STRIPE_WEBHOOK_SECRET }}" \ --build-arg VITE_CMS_CLIENT="${{ vars.VITE_CMS_CLIENT }}" \ --build-arg VITE_KEYSTATIC_CONTENT_PATH="${{ vars.VITE_KEYSTATIC_CONTENT_PATH }}" \ --build-arg VITE_LOCALES_PATH="${{ vars.VITE_LOCALES_PATH }}" \ --build-arg MAILER_PROVIDER="${{ secrets.MAILER_PROVIDER }}" \ --build-arg EMAIL_SENDER="${{ secrets.EMAIL_SENDER }}" \ --build-arg EMAIL_HOST="${{ secrets.EMAIL_HOST }}" \ --build-arg EMAIL_PORT="${{ secrets.EMAIL_PORT }}" \ --build-arg EMAIL_USER="${{ secrets.EMAIL_USER }}" \ --build-arg EMAIL_PASSWORD="${{ secrets.EMAIL_PASSWORD }}" \ --build-arg EMAIL_TLS="${{ secrets.EMAIL_TLS }}" \ --build-arg VITE_TEAM_NAVIGATION_STYLE="${{ vars.VITE_TEAM_NAVIGATION_STYLE }}" \ --build-arg VITE_USER_NAVIGATION_STYLE="${{ vars.VITE_USER_NAVIGATION_STYLE }}" \ --build-arg VITE_THEME_COLOR="${{ vars.VITE_THEME_COLOR }}" \ --build-arg VITE_THEME_COLOR_DARK="${{ vars.VITE_THEME_COLOR_DARK }}" \ --build-arg SIGN_IN_PATH="${{ vars.SIGN_IN_PATH }}" \ --build-arg SIGN_UP_PATH="${{ vars.SIGN_UP_PATH }}" \ --build-arg TEAM_ACCOUNTS_HOME_PATH="${{ vars.TEAM_ACCOUNTS_HOME_PATH }}" \ --build-arg INVITATION_PAGE_PATH="${{ vars.INVITATION_PAGE_PATH }}" \ --build-arg VITE_DISPLAY_TERMS_AND_CONDITIONS_CHECKBOX="${{ vars.VITE_DISPLAY_TERMS_AND_CONDITIONS_CHECKBOX }}" env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

Configure your secrets and environment variables in GitHub Actions.

Note: It's recommended to use GitHub's secrets and variables for this deployment process, as Fly.io's secrets may not function as expected in this context.

Please review the list of environment variables in the deployment command above and adjust them according to your project's specific requirements. Remove any variables that are not applicable to your setup.

Ensure all necessary secrets and variables are properly set in your GitHub repository settings before running the deployment workflow.


Subscribe to our Newsletter
Get the latest updates about React, Remix, Next.js, Firebase, Supabase and Tailwind CSS