Self-hosting is a great way to have control over your deployment infrastructure (you own the data) and a great way to keep the costs down (you pay a fixed amount per month). One of the smoothest way to manage your self-hosted deployment is Coolify.
Deploying Wasp apps to Coolify is straightforward:
- create your Coolify apps (client, server and db)
- build your app's Docker images (e.g. using Github Actions),
- trigger Coolify to pull the Docker images and deploy them.
It'll take you ~1 hour depending on your level of experience with servers and Github Actions.
You should have Coolify installed and set up on your server. I'll be using Hetzner to rent my server.
Follow the Coolify install instructions: https://coolify.io/self-hosted
To get Coolify apps working with your domain - you'll need to point a A record to your server IP:
-
To use
myapp.comas your client domain, point theArecord with the value of@to your server IP. -
To use
api.myapp.comas your server domain, point theArecord with the value ofapito your server IP.
We'll set up the domains for our server and client apps below.
Tip
You can point an A record with value of coolify to your server IP and set Instance's Domain in the Settings as https://coolify.myapp.com so you can access the Coolify dashboard via the subdomain. Read more on Coolify domain setup here: https://coolify.io/docs/knowledge-base/dns-configuration
- Create a new project or use the default one
- Create a new resource, select
PostgreSQL- I pick the default PostgreSQL 16 variant, you can try others as well - they should work just fine
- Name it
db(to keep things clean) - Click
Startto set up the database - Copy the
Postgres URL (internal)- we'll need it later
- Create a new resource, select
Docker ImageunderDocker Based- We'll write
ghcr.io/<your-github-username>/<something>as the Docker image name since we'll be using the Github Container Registry (ghcr.io) to store our Docker images - I'll write
ghcr.io/infomiho/pokemon-serverfor my server image (I'll usepokemon-serveras my app name later in the Github Action) - Name the app
serverto keep things clean - Set the
Domainstohttps://api.<your-domain> - Set the
Docker Image Tagtomainsince we'll use that tag later - Set the
Port Exposesto3001 - Make sure to hit the
Savebutton after you change stuff
- We'll write
- Create a new resource, select
Docker ImageunderDocker Based- I'll write
ghcr.io/infomiho/pokemon-clientfor my client image (I'll usepokemon-clientas my app name later in the Github Action) - Name the app
clientto keep things clean - Set the
Domainstohttps://<your-domain> - Set the
Docker Image Tagtomainsince we'll use that tag later - Set the
Port Exposesto8043 - Make sure to hit the
Savebutton after you change stuff
- I'll write
Let's go back into the server app and configure the required env vars:
- Go under
Environment Variables - Add the following env vars:
DATABASE_URLwith value of thePostgres URL (internal)JWT_SECRETgenerate it with some online generatorPORTset it to3001WASP_WEB_CLIENT_URLset it tohttps://<your-domain>WASP_SERVER_URLset it tohttps://api.<your-domain>
- Add any other env vars you might have defined locally in the
.env.serverfile
- In your app root dir create a new
.githubfolder - Inside of
.githubfolder, create a newworkflowsfolder - Copy the
deploy.ymlfile from this gist to theworkflowsfolder
Once you copy the deploy.yml, make sure to modify the:
WASP_VERSIONenv varSERVER_APP_NAMEenv var - this will be used in the Docker image nameSERVER_APP_URLenv varCLIENT_APP_NAMEenv var - this will be used in the Docker image name
The DOCKER_REGISTRY, DOCKER_REGISTRY_USERNAME and DOCKER_REGISTRY_PASSWORD env vars will work out of the box for Github Container Registry.
Warning
If your app is located in the app folder (e.g. Open Saas has the app in the app folder) you can use the action as-is.
If your app is not in the app folder, follow the comments to modify some paths.
The Github Action depends on some repository secrets to work properly - these are some values that can't be public. You add them by going into your repository Settings and then find Secrets and variables and select Actions.
Let's add the:
SERVER_COOLIFY_WEBHOOKsecret- Go to your
serverapp - Click
Webhooksand copy theDeploy Webhook - Paste it as the secret value
- Go to your
CLIENT_COOLIFY_WEBHOOKsecret- Go to your
clientapp - Click
Webhooksand copy theDeploy Webhook - Paste it as the secret value
- Go to your
COOLIFY_TOKENsecret- Go to
Settingsand enable API - Go to
Keys & Tokensnext and clickAPI tokens - Create a new API token with
Root Access - Copy it and paste it under the secret value
- Go to
You can move your domain's nameservers to Cloudflare to get the benefits of their CDN and DDoS protections.
I've had to set my SSL mode to Full to get it working.

@infomiho I think the instructions for getting the
COOLIFY_TOKENmay be outdated. It seems to just be a "Keys & Tokens" sidebar menu now, rather than being in settings. Steps:rootpermissions.Also this didn't work for me as I'm using TS Spec. I added a ts-setup step to the yaml, which seems to have fixed it:
Side note, should the installation URL be updated to wasp.sh?
And for deploying a private GHCR container:
read:packagesdocker login ghcr.ioThanks for the guide!