Deploying a Wasp app to a VPS
Hey! I'm trying to deploy the Wasp backend app on Hetzner using Caddy, following these guidelines https://gist.github.com/infomiho/80f3f50346566e39db56c5e57fefa1fe.
However, my reverse proxy doesn’t seem to be working. I checked with netstat inside the Docker container, and it looks like no ports are being exposed.
Do you have any idea what might be going wrong?
22 Replies
Hi @Ramon. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
Based on the knowledge sources, here are some key points and possible reasons why your reverse proxy (Caddy) isn’t working with your Wasp app on Hetzner:
1. Docker Container Ports Must Be Exposed
- If netstat inside your Docker container shows no ports being exposed, it likely means your Docker container was not started with the correct
-p (publish) or --expose flags. You need to map the internal ports (e.g., 3000 for client, 3001 for server) to the host so Caddy can access them. For example:
- If you don’t expose the ports, services inside the container won’t be reachable from outside, including by your reverse proxy.
2. Wasp Default Ports
- By default, Wasp runs the client on port 3000 and the server on port 3001. If these are not available or you want to use different ports, you need to configure them both in your Docker setup and in your environment variables. There are discussions and examples about changing these ports and the required environment variables in the knowledge sources. For example, you can set the PORT environment variable for the server and REACT_APP_API_URL for the client to point to the correct backend URL. Make sure these are set correctly in your Docker environment and Caddy config as well Make it easy to use different ports (than 3000 and 3001) during Wasp development.
3. Reverse Proxy Configuration
- If Caddy is running on the host, it needs to proxy requests to the correct host and port where your Wasp app is running. If the Docker container’s ports are not published to the host, Caddy cannot reach them.
4. Docker Compose
- If you are using Docker Compose, ensure your docker-compose.yml exposes the necessary ports and that your Caddy service is configured to proxy to the correct internal Docker network addresses. There is a mention that a typical setup would include a PostgreSQL container, server container, client container, and an optional reverse proxy like Caddy or Nginx Deployment to bare metal.
Summary of what to check:
- Ensure you are exposing the necessary ports when running your Docker containers.
- Double-check your Caddy configuration to make sure it proxies to the correct host:port.
- Make sure your environment variables inside the containers are set to match the ports you are using.
If you provide your Docker run or compose commands and your Caddyfile, I can help you debug further. But based on your description, the most likely issue is that the Docker container ports are not being published to the host, so Caddy cannot reach your Wasp app.
Let me know if you need more specific help with your Docker or Caddy configuration!
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
Sorry @Ramon, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?sudo tee /etc/caddy/Caddyfile > /dev/null <<EOF
api.mysaas.com {
reverse_proxy 127.0.0.1:3001
}
EOF
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
PORT=3001
docker run -d \ --name mysaas \ -p ${{ vars.PORT }}:${{ vars.PORT }} \ --restart unless-stopped \ -v /var/log/mysaas:/var/log \ -v /var/mysaas/uploads:/var/mysaas/uploads \ -e DATABASE_URL="${{ secrets.DATABASE_URL }}" \ -e WASP_WEB_CLIENT_URL="${{ vars.WASP_WEB_CLIENT_URL }}" \ -e WASP_SERVER_URL="${{ vars.WASP_SERVER_URL }}" \ -e JWT_SECRET="${{ secrets.JWT_SECRET }}" \ -e PORT="${{ vars.PORT }}" \ -e SMTP_HOST="${{ vars.SMTP_HOST }}" \ -e SMTP_USERNAME="${{ secrets.SMTP_USERNAME }}" \ -e SMTP_PASSWORD="${{ secrets.SMTP_PASSWORD }}" \ -e SMTP_PORT="${{ vars.SMTP_PORT }}" \ -e GOOGLE_CLIENT_ID="${{ secrets.GOOGLE_CLIENT_ID }}" \ -e GOOGLE_CLIENT_SECRET="${{ secrets.GOOGLE_CLIENT_SECRET }}" \ mysaas @kapa.ai
docker run -d \ --name mysaas \ -p ${{ vars.PORT }}:${{ vars.PORT }} \ --restart unless-stopped \ -v /var/log/mysaas:/var/log \ -v /var/mysaas/uploads:/var/mysaas/uploads \ -e DATABASE_URL="${{ secrets.DATABASE_URL }}" \ -e WASP_WEB_CLIENT_URL="${{ vars.WASP_WEB_CLIENT_URL }}" \ -e WASP_SERVER_URL="${{ vars.WASP_SERVER_URL }}" \ -e JWT_SECRET="${{ secrets.JWT_SECRET }}" \ -e PORT="${{ vars.PORT }}" \ -e SMTP_HOST="${{ vars.SMTP_HOST }}" \ -e SMTP_USERNAME="${{ secrets.SMTP_USERNAME }}" \ -e SMTP_PASSWORD="${{ secrets.SMTP_PASSWORD }}" \ -e SMTP_PORT="${{ vars.SMTP_PORT }}" \ -e GOOGLE_CLIENT_ID="${{ secrets.GOOGLE_CLIENT_ID }}" \ -e GOOGLE_CLIENT_SECRET="${{ secrets.GOOGLE_CLIENT_SECRET }}" \ mysaas @kapa.ai
Let me check the knowledge base for you again. One moment! :books:
Sorry @Ramon, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist:
Perhaps try again in 15mins?
Hey @Ramon, need human help debugging this?
That would be greatly appreciated! Do you have any ideas, or does it ring a bell?
Not right of the bat, but I'm sure we can figure it out
First, did you manage to get this step working?

Yes, when I visited the server’s IP in the browser, I see Caddy’s default static page.
After some lengthy troubleshooting with ChatGPT 🤣 , it seems the issue might be that the Docker container isn’t exposing any ports. Could you confirm whether, in your test applications, the container is exposing port 3001?
Nevermind, the issue seems to be related to the initial migration on the Supabase database
Fixed it — I was using the wrong connection string. With Supabase, it’s important to select ORMs → Prisma in the setup wizard, since that provides a different connection string!
I'm currently encountering some challenges with the next steps, specifically with the 'direct' connection string option. Occasionally, I receive errors such as "can't connect to the database" or "max clients reached" from Pgboss.
I noticed in the recommendations that using a 'transaction pooler' connection string is suggested, but this approach doesn't work with Prisma Migrations.
Given these guidelines, would it be possible to configure the DIRECT_URL environment variable in the schema.prisma file when using WASP?
Additionally, I notice there are many open database connections. Is there any recommended way to close them when stopping the WASP docker container to deploy a new version?
When adding the directUrl in schema.prisma it fixes the instability in production but makes 'wasp start' fail in local. Would it be possible for you to support this?
Wohooo @Ramon, you just became a Waspeteer level 4!
Hmm, can you elaborate on this so we can document it? How did Supabase get into the mix, and how did that affect Wasp's ports?
I noticed in the recommendations that using a 'transaction pooler' connection string is suggested, but this approach doesn't work with Prisma Migrations.Can you point me to this resource? Who recommends it? Why do you think you need a
DIRECT_URL?
Let's start with the exact problem you're having that pushed you towards using DIRECT_URL just so we avoid the XY problem.Hey@sodic, yes, sorry — I was sharing updates as I made progress. Here’s the summary:
- I decided to use Supabase as the database for my Wasp project.
- At first, I only configured the connection string. The connection wasn’t actually working, but I didn’t notice because the container logs showed no errors. That made me think the issue was related to Docker ports, but it turned out to be the wrong connection string type ('transaction pooler').
- Then I tried the direct connection string provided by the Supabase Wizard. That made the website work, but the database connection was unstable and kept dropping.
- After carefully reviewing the recommendations from the Supabase Wizard and some forums, I learned that when using Supabase with Prisma, you should configure both:
-
DATABASE_URL with the transaction pooler
- DIRECT_URL with the direct connection string (used specifically for Prisma migrations)
This avoids instability.
- I manually updated the schema.prisma file by adding directUrl = env("DIRECT_URL"), and now everything works perfectly.
- The only remaining issue is that this setup causes the wasp start command to fail locally.
- That’s why I think it would be great if this could be officially supported, since other Supabase + Wasp users will likely run into the same problem.
Thank you so much for all your time and support!
Here are some useful references:
- https://supabase.com/docs/guides/database/prisma (click 'serverless deployments')
- https://stackoverflow.com/questions/77990019/cant-reach-database-server-at-aws-0-us-east-1-pooler-supabase-com5432
- https://github.com/prisma/prisma/discussions/24881
Hey @Ramon, this is excellent!
We'll learn quite a bit from your use case so thatnk you for describing it so thoroughly - very clear and easy to follow
Ok, so if I got it right, all the stuff with too many database connections and "max clients reached" were red herrings. All I should focus on is:
- Documenting this for our users
- Figuring out why
wasp start fails
Assuming that's the case, can you share the error message wasp start fails with?Hi @sodic, when I run
wasp start locally, I get the error "Cannot connect to database". However, if I comment out the directUrl in schema.prisma, everything works as expected (wasp db start is running on the other cli)
@Ramon When you run
wasp start db, the connection string for the database will be displayed, e.g.
If you don't already have one, create a .env.server file in your application root, then set the DIRECT_URL env var to this valueHey @Ramon, thanks again. I'll tag @miho here since I'm away.
Miho, Ramon gave a ice explanation of his use case and the problem. Could you drive this to the end (create an issue, see what we can do for Ramon, etc.)?
@Filip I had a lot of similar problems, just like you, with deployment and make wasp running with docker. In the end, I gave up because it seems that Wasp mainly works well with support from other server providers, check website and commands. Everything works easily with Railway, so for me, that was the simplest solution, as it was for my friend devOps.
Wohooo @Aleksandar Okiljevic, you just became a Waspeteer level 1!
@Aleksandar Okiljevic have you checked out docs on self hosting? https://wasp.sh/docs/deployment/deployment-methods/self-hosted
Self-Hosted | Wasp
If you have your server or rent out a server, you can self-host your Wasp apps. Self-hosting your apps gives you full control over your apps and their data. It can be more cost-effective than a cloud provider since you can deploy multiple apps on a single server. However, you'll need to manage the server yourself, which can be time-consuming and...
Thanks @Filip, @miho, Just let me know if there’s anything else I can do to help with this. It would be really helpful to have support for the
directUrl variable in schema.prismaDid you see my message? All you have to do is set the environment variable
I skipped this one, sorry. Will try this soon. Thanks for your help!
Thanks, it works when I set the
DIRECT_URL manually. I still think it would be great if the wasp start command could automatically inject this value, just like it does with DATABASE_URL.@Ramon I've added this issue to upgrade our docs to avoid these kind of problems in the future 🙂 https://github.com/wasp-lang/wasp/issues/3259
GitHub
Using databases with connection poolers with Prisma and
directUrl...When using databases with connection poolers, the users usually need to setup the database URL (used for transactions) and the direct URL (used for migrations). Supabase Example When following Supa...