Skip to content

Instantly share code, notes, and snippets.

@infomiho
Last active March 25, 2025 18:11
Show Gist options
  • Save infomiho/ad6fade7396498ae32a931ca563a4524 to your computer and use it in GitHub Desktop.
Save infomiho/ad6fade7396498ae32a931ca563a4524 to your computer and use it in GitHub Desktop.
Deploying Wasp apps to Coolify

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.

Installing Coolify

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

Deploying your app

Adding your domain

To get Coolify apps working with your domain - you'll need to point a A record to your server IP:

  • To use myapp.com as your client domain, point the A record with the value of @ to your server IP.

  • To use api.myapp.com as your server domain, point the A record with the value of api to 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 the Coolify apps

  1. Create a new project or use the default one
  2. 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 Start to set up the database
    • Copy the Postgres URL (internal) - we'll need it later
  3. Create a new resource, select Docker Image under Docker 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-server for my server image (I'll use pokemon-server as my app name later in the Github Action)
    • Name the app server to keep things clean
    • Set the Domains to https://api.<your-domain>
    • Set the Docker Image Tag to main since we'll use that tag later
    • Set the Port Exposes to 3001
    • Make sure to hit the Save button after you change stuff
  4. Create a new resource, select Docker Image under Docker Based
    • I'll write ghcr.io/infomiho/pokemon-client for my client image (I'll use pokemon-client as my app name later in the Github Action)
    • Name the app client to keep things clean
    • Set the Domains to https://<your-domain>
    • Set the Docker Image Tag to main since we'll use that tag later
    • Set the Port Exposes to 8043
    • Make sure to hit the Save button after you change stuff

Configure Server Env Vars

Let's go back into the server app and configure the required env vars:

  1. Go under Environment Variables
  2. Add the following env vars:
    • DATABASE_URL with value of the Postgres URL (internal)
    • JWT_SECRET generate it with some online generator
    • PORT set it to 3001
    • WASP_WEB_CLIENT_URL set it to https://<your-domain>
    • WASP_SERVER_URL set it to https://api.<your-domain>
  3. Add any other env vars you might have defined locally in the .env.server file

Add the Github Action

  1. In your app root dir create a new .github folder
  2. Inside of .github folder, create a new workflows folder
  3. Copy the deploy.yml file from this gist to the workflows folder

Configure the Github Action

Once you copy the deploy.yml, make sure to modify the:

  1. WASP_VERSION env var
  2. SERVER_APP_NAME env var - this will be used in the Docker image name
  3. SERVER_APP_URL env var
  4. CLIENT_APP_NAME env 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 on lines 70, 87, 89, 98 and 100 to modify some paths.

Adding the repository secrets

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:

  1. SERVER_COOLIFY_WEBHOOK secret
    • Go to your server app
    • Click Webhooks and copy the Deploy Webhook
    • Paste it as the secret value
  2. CLIENT_COOLIFY_WEBHOOK secret
    • Go to your client app
    • Click Webhooks and copy the Deploy Webhook
    • Paste it as the secret value
  3. COOLIFY_TOKEN secret
    • Go to Settings and enable API
    • Go to Keys & Tokens next and click API tokens
    • Create a new API token with Root Access
    • Copy it and paste it under the secret value

Using Cloudflare (bonus)

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.

name: "Deploy"
on:
push:
branches:
- "main"
# This will make sure that only one deployment is running at a time
concurrency:
group: deployment
cancel-in-progress: true
env:
WASP_VERSION: "0.15.1"
# Put your server app name here
SERVER_APP_NAME: "pokemon-server"
# After you know the server URL, put the URL here
SERVER_APP_URL: "https://api.<your-domain>"
# Put your client app name here
CLIENT_APP_NAME: "pokemon-client"
DOCKER_REGISTRY: "ghcr.io"
DOCKER_REGISTRY_USERNAME: ${{ github.repository_owner }}
# This secret is provided by GitHub by default and is used to authenticate with the Container registry
DOCKER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
jobs:
build-and-push-images:
permissions:
contents: read
packages: write
runs-on: ubuntu-latest
# REMOVE this whole block if your app is not in the `app` folder
defaults:
run:
working-directory: ./app
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ env.DOCKER_REGISTRY_USERNAME }}
password: ${{ env.DOCKER_REGISTRY_PASSWORD }}
- name: (server) Extract metadata for Docker
id: meta-server
uses: docker/metadata-action@v5
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_REGISTRY_USERNAME }}/${{ env.SERVER_APP_NAME }}
- name: (client) Extract metadata for Docker
id: meta-client
uses: docker/metadata-action@v5
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_REGISTRY_USERNAME }}/${{ env.CLIENT_APP_NAME }}
- name: Install Wasp
shell: bash
run: curl -sSL https://get.wasp-lang.dev/installer.sh | sh -s -- -v ${{ env.WASP_VERSION }}
- name: Build Wasp app
shell: bash
run: wasp build
- name: (client) Build
shell: bash
run: |
cd ./.wasp/build/web-app
REACT_APP_API_URL=${{ env.SERVER_APP_URL }} npm run build
- name: (client) Prepare the Dockerfile
shell: bash
run: |
cd ./.wasp/build/web-app
echo "FROM pierrezemb/gostatic" > Dockerfile
echo "CMD [\"-fallback\", \"index.html\", \"-enable-logging\"]" >> Dockerfile
echo "COPY ./build /srv/http" >> Dockerfile
- name: (server) Build and push Docker image
uses: docker/build-push-action@v6
with:
# REMOVE the `app` bit from the path if your app is not in the `app` folder
context: ./app/.wasp/build
# REMOVE the `app` bit from the path if your app is not in the `app` folder
file: ./app/.wasp/build/Dockerfile
push: true
tags: ${{ steps.meta-server.outputs.tags }}
labels: ${{ steps.meta-server.outputs.labels }}
- name: (client) Build and push Docker image
uses: docker/build-push-action@v6
with:
# REMOVE the `app` bit from the path if your app is not in the `app` folde
context: ./app/.wasp/build/web-app
# REMOVE the `app` bit from the path if your app is not in the `app` folder
file: ./app/.wasp/build/web-app/Dockerfile
push: true
tags: ${{ steps.meta-client.outputs.tags }}
labels: ${{ steps.meta-client.outputs.labels }}
# You can get the webhook URLs from the Coolify dashboard
# Put them in the repository secrets under CLIENT_COOLIFY_WEBHOOK and SERVER_COOLIFY_WEBHOOK
- name: Trigger Deploy Webhooks
env:
CLIENT_COOLIFY_WEBHOOK: ${{ secrets.CLIENT_COOLIFY_WEBHOOK }}
SERVER_COOLIFY_WEBHOOK: ${{ secrets.SERVER_COOLIFY_WEBHOOK }}
COOLIFY_TOKEN: ${{ secrets.COOLIFY_TOKEN }}
run: |
curl "${{ env.CLIENT_COOLIFY_WEBHOOK }}" --header 'Authorization: Bearer ${{ env.COOLIFY_TOKEN }}'
curl "${{ env.SERVER_COOLIFY_WEBHOOK }}" --header 'Authorization: Bearer ${{ env.COOLIFY_TOKEN }}'
@chirag38-unity
Copy link

Im getting the error in coolify logs that I am unauthorised to pull the images

@ABckh
Copy link

ABckh commented Mar 25, 2025

@chirag38-unity
This will help:

  1. Create "classic" Github access token
  2. Add packages read permission
  3. Save the generated ghp_... key
  4. ssh into your server
  5. docker login ghcr.io -u USERNAME
  6. Enter your token

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment