Self-Hosting NextJS + Supabase
I'll admit it - I like building with NextJS + Supabase. I started using this combo ~9 months ago, and after a quick initial learning curve, I found I could build pretty quickly with this combo.
The downside though is that I didn't know how to self-host these two things, so I was stuck deploying to Vercel and Supabase Cloud. Both of which have generous free tiers, and honestly do a great job of making it easy to just focus on app dev.
The problem is costs can balloon quickly depending on the type of app you're hosting with them, and depending on how you architect your app. If you build it poorly, you'll be feeling the pain when it comes time to pay your monthly bill.
But fortunately, I've got a background where I became very comfortable with Linux and working in a terminal and have self hosted things in the past. So self-hosting was an option, I was just too lazy at the time to figure it out. But with my latest app (tokilearn.com), I decided it was time to bite the bullet and try self-hosting these things.
There were a LOT of little issues, and I'll try to remember all of them. I did post something recently about tips and tricks I discovered to get self-hosted Supabase working, so check that out if you're interested. But this will not rehash any of that stuff.
Supabase
I operated under the assumption that the Supabase repo is up to date, and that their docker
folder had a good, default setup for how to self-host Supabase. I think this assumption is good, but I saw some other guides mention MinIO being necessary for object storage if you wanted to use Supabase's storage service. But Supabase doesn't include anything like this in their default docker setup on the repo. I don't currently need object storage, so I'm not worried about this. And I also found you can just stored stuff to disk through the storage service if you really need to.
The biggest hurdle was just figuring out how to limit the resource usage for Supabase overall. I could have used a docker-wide setting, but I really wanted to keep Supabase isolated to avoid it eating into the resources for my NextJS app.
Resource Usage
I saw somewhere online that the minimum requirements were 4GB of memory and 2 vCPU. Not sure if that's actually right, I don't think I found a definitive answer on this. But it sounded reasonable.
I opted for a machine with 2 vCPU and 8GB of memory to start with. This was going to be tight for running both my NextJS app and Supabase on the same server. But I figured it'd be fine to start with and I could upgrade later.
I've heard some situations where people self-hosted Supabase on a machine and it acted like a hungry, hungry hippo and just devoured resources. So I wanted to specify some resource limits for each service (and there are like 10 services by default). This way, it could devour all the resources assigned to it without affecting the NextJS app and anything else on the system.
I used Grok to help me assign CPU and mem usage limits to each service and wanted to limit Supabase to ~1.5 vCPU and 4GB of memory. I quickly found that wasn't going to work... the CPU usage was the bottleneck that broke everything. Specifically, supabase-analytics
was the service that needed more juice. And a bunch of other services rely on this analytics service. So if it can't start, almost nothing else will.
(Technically, I think it was starting, but it was just so slowwwww... and a higher CPU limit was the answer. Kong (their API service) also had some issues with lower cpu/mem limits. So that one got boosted as well. I probably could have tried to untangle things and remove unnecessary services, but I didn't feel like playing that game. I don't know what's necessary or not, so I just assumed it all was.)
After spending ~1 hour trying to tweak things in that range of ~1.5 CPU usage, I found that nothing worked. I then decided I should just spring for a larger box and went for one that costs ~$50/mo. This sounds like a lot, but once you're on the pro Vercel and Supabase plans, you're paying $45/mo + extra in fees. So this will still end up saving money. And I got quite bit more resources than I need, so I have plenty of room for things to move around and grow.
Below are some snippets from my docker-compose.yml
file that share the resource allocation in case you need it:
services:
studio:
container_name: supabase-studio
# Other stuff...
deploy:
resources:
limits:
cpus: '0.2' # 200m
memory: 256M
kong:
container_name: supabase-kong
# Other stuff...
deploy:
resources:
limits:
cpus: "0.5" # 500m
memory: 2048M
auth:
container_name: supabase-auth
# Other stuff...
deploy:
resources:
limits:
cpus: "0.2" # 200m
memory: 512M
rest:
container_name: supabase-rest
# Other stuff...
deploy:
resources:
limits:
cpus: "0.2" # 200m
memory: 256M
realtime:
container_name: realtime-dev.supabase-realtime
# Other stuff...
deploy:
resources:
limits:
cpus: "0.15" # 150m
memory: 256M
storage:
container_name: supabase-storage
# Other stuff
deploy:
resources:
limits:
cpus: "0.2" # 200m
memory: 512M
imgproxy:
container_name: supabase-imgproxy
# Other stuff
deploy:
resources:
limits:
cpus: "0.1" # 100m
memory: 128M
meta:
container_name: supabase-meta
# Other stuff
deploy:
resources:
limits:
cpus: "0.1" # 100m
memory: 256M
functions:
container_name: supabase-edge-functions
# Other stuff
deploy:
resources:
limits:
cpus: "0.15" # 150m
memory: 256M
analytics:
container_name: supabase-analytics
# Other stuff...
deploy:
resources:
limits:
cpus: "0.6" # 600m
memory: 512M
db:
container_name: supabase-db
# Other stuff...
deploy:
resources:
limits:
cpus: "1.0" # 1000m
memory: 2048M
vector:
container_name: supabase-vector
# Other stuff...
deploy:
resources:
limits:
cpus: "0.1" # 100m
memory: 256M
supavisor:
container_name: supabase-pooler
# Other stuff...
deploy:
resources:
limits:
cpus: "0.4" # 400m
memory: 256M
NextJS
It was WAY easier hosting the NextJS app. I had Claude create for me a Dockerfile
and docker-compose.yml
file. You can pretty much just take whatever it gives you, and it'll likely work.
It was way easier to host the app than I thought it would be. When I first started using NextJS, I thought there was a bunch of magic going on in the background to run it that would make it tough to self-host.
Granted, I'm probably still pretty noob-ish with NextJS, but I think my current setup will work well enough for a while.
Docker
I opted to host both Supabase and the NextJS app using Docker. I could have set Docker-wide resource limits for all deployed services, but I was worried about Supabase taking more than its share. So I had to add specific resource limits to EACH Supabase service separately.
And I also added some sane limits for my NextJS app as well. And I still have CPU and lots of memory to spare.
I'm not super experienced with Docker, so I ran into one issue after setting everything up. Basically, docker-compose files look for a local .env file when it runs to get its sensitive environment variables. I forgot that when I first ran docker compose up -d
for the NextJS app, I had some default values in there. And running docker compose down
followed by up -d
apparently doesn't rebuild anything. So my build was stuck using old environment variables and caused some headache and confusion for a while. So when you want to pull in new changes to your app, you need to run docker compose up -d --build
to rebuild it as well.
Deploying
My process for deploying isn't as slick as with Vercel. It's manual, but very simple. SSH into the box, go into my app directory, git pull
down the changes, run any migrations to update database schema if necessary, then run docker compose up -d --build
to rebuild the app with the new changes. Done. Easy breezy.