This is a Docker Compose setup for self-hosting Supabase. It has been tweaked to be compatible with hosting on Railway. It is being closely maintained and updated as the Supabase platform adds new features.
PG On Rails is a passion project that combines two of my favourite things: Supabase and Railway! At first I called it "Supabase On Railway", but the name "PG On Rails" just felt like an obvious way to honor both Postgres and Railway.
PG On Rails is local-first. It is my mission to make the developer experience with Supabase better than ever before, which means that we not only need to run the entire stack locally, but we need to see behind the magic, and even wield some of the magic for ourselves. By moving every service into its own directory, we open up the option to add configuration, custom app logic, and take advantage of the modern deployment pattern of watch paths in monorepos.
My longterm vision is to make PG On Rails the best strategy for bootstrapping, building and self-hosting Supabase projects, on the Railway platform and beyond.
Setup environment and volumes
./setup.sh
Run the app locally
docker compose up
Run DB migrations for the NextJS site
./migrate-local.sh
Visit the supabase studio at http://localhost:8000
Visit the frontend site in dev mode at http://localhost:5173
Happy hacking!
Get working on real application features in seconds. We go the extra mile to make default configuration minimal while still covering everything needed to run out-of-the-box. Avoid drowning in config, and opt in to more hackability as needed.
Every service gets its own directory, so watchpaths just work. Add new functions, configuration files, migrations, and anything else to a service's repo, commit your work, and Railway (or your CI/CD of choice) will trigger a new build.
Reduce context-switching and host as much of your stack as possible on the same platform. For NextJS, Django, htmx or any server-rendered frontend, get fast and secure access to Supabase data APIs via the shared internal network.
For the hackers. Configure every aspect of your Supabase application and version it in code:
- Email templates
- Third-party auth providers
- Environment variables
- Networking settings
- Railway config-as-code
-
Visit the template page for PG On Rails and click "Deploy Now".
-
Use the Supabase self-hosting tool to generate a JWT secret and keys for your project. Add them to the input fields provided by the
Postgres
service.
- Wait for the project to deploy.
- Eject from the template repo and Railway will create a fork for you on GitHub.
- On each service, turn on
Wait For CI
and addWatch Paths
to make CI/CD more targeted.
- A service's watch path should be its own
Root Directory
, which can be found at the top of theSettings
panel, followed by/**/*
. For example, theSite
service builds from the/site
directory within this repo, therefore its watch path is/site/**/*
. - You must manually do this on all 13 services in your project in order to configure Railway's CI/CD.
-
Copy the
DB_PUBLIC_CONNECTION_STRING
from thePostgres
service. -
Visit your new repo and add the
DB_URL
secret to GitHub Actions secrets. -
Manually run the DB migrations action.
- Clone your repo and begin building features locally. Push to GitHub and watch Railway CI/CD work wonders!
We included a frontend app in the stack and named it site
. The frontend site is a NextJS app built with create-next-app
, tailwindCSS
and shadcn/ui
. It includes basic auth functionality so you can begin building user experiences out of the box.
The project is setup so that running the Docker Compose stack locally runs the site in dev mode. See how this is possible in docker-compose.yml
:
services:
site:
build:
context: ./site
dockerfile: dev.Dockerfile
volumes:
- ./site:/app
The local development experience, which runs on Docker Compose, points to a dev.Dockerfile
in the site
repo. This dockerfile runs the NextJS dev server. For production, however, Railway looks for a Dockerfile
by default (no dev
prefix), and will deploy using the Dockerfile
which builds and serves the optimized site.
The other strategy which enables smooth local development inside Docker, is mounting the entire site
directory as a volume inside the dev container (volumes: - ./site:/app
). This exposes the codebase from your local filesystem inside the container, where the dev server can pick up any changes and deliver that hot-reload experience we all love.
By default, mailing is disabled. Once a user signs up with their email and password, their email is "auto-confirmed" by the auth server and they are signed in.
The auth server requires an SMTP server to send transactional emails. In my experience, the quickest way to get up and running in both local and non-production cloud environments, is through a gmail account with an app password.
Log in to the Google account you want all transactional emails to come from. Visit the following link to create a Google app password.
Make sure the email signup and SMTP environment variables are set:
GOTRUE_MAILER_AUTOCONFIRM=false
GOTRUE_SMTP_ADMIN_EMAIL=johndoe@gmail.com
GOTRUE_SMTP_USER=myapp@gmail.com
GOTRUE_SMTP_PASS="abcd efgh ijkl mnop"
GOTRUE_SMTP_HOST=smtp.gmail.com
GOTRUE_SMTP_PORT=587
GOTRUE_SMTP_SENDER_NAME: "PG On Rails"
NOTE - SMTP traffic on Railway is only allowed for the Pro Plan and above.