Deploy a Wasp App on SnapDeploy: Server, Client and PostgreSQL from One Repository

Two Dockerfiles in your Wasp project, two containers from the same GitHub repository, a managed PostgreSQL next to them. SnapDeploy installs the Wasp CLI and runs wasp build on its own builders, so you push source, not build output, and every push redeploys.

Free tier: 4 containers, 512 MB each, 100 hours a month, 10 deploys a day. No credit card. Always-On from $12 a month, PostgreSQL from $1 for 12 hours or $29 a month.

Last verified 6 October 2026 with Wasp 0.25 and Node.js 24: the sample repository below was built and run with these exact files. Every number is from the free-tier docs and the add-on pricing page.

Where a Wasp App Can Be Hosted in 2026

Wasp's own documentation has guides for the providers below. What each one gives a server, a client and a database, from the providers' own pages on 6 October 2026.

Host How the server gets there Client PostgreSQL Free to start
SnapDeployPush the repo; a Dockerfile installs Wasp and builds on the platform's ARM64 buildersSecond container from the same repo (nginx)Managed add-on: $1 for 12 hours, Mini $29 a month4 containers, 100 hours a month, no card; sleeps after 15 minutes
RenderBlueprint builds from source on RenderStatic site serviceFree Postgres, deleted after 30 days; paid from there750 instance-hours, 0.1 CPU, sleeps after 15 minutes; 10 to 15 minute builds on the free tier per Wasp's guide
RailwayRailway CLI deploys the built outputSeparate serviceRailway PostgresOne-time $5 trial credit, then $5 a month
Fly.iofly launch from .wasp/out with the generated DockerfileElsewhere (the guide suggests Netlify)Fly PostgresNo free allowance for new organisations; pay as you go

Isolation: every SnapDeploy container, free tier included, runs as its own AWS Fargate task with a reserved 0.25 vCPU and 512 MB. AWS runs each Fargate task on a single-use, single-tenant compute instance, so your Wasp server never shares a host with another customer's app. Render's free instance is 0.1 CPU and 512 MB, Railway's trial runs on shared vCPU cores (its own wording), and Fly.io gives each Machine its own VM but has no free tier.

Sources: wasp.sh/docs (deployment guides for Render, Railway and Fly.io, read 6 October 2026); render.com/docs/free and render.com/docs/compute-plans; railway.com/pricing and docs.railway.com/pricing/free-trial; fly.io/docs/about/pricing and docs.fly.io/machines; aws.amazon.com/fargate/faqs (task isolation). SnapDeploy figures from /docs/free-tier and /addon-pricing.

Deploy the Wasp Server

Wasp needs PostgreSQL in production and will not build with SQLite, so switch the Prisma datasource first, run wasp db migrate-dev once locally and commit the migrations/ folder. Then add the three files below to the project root.

1.Add Dockerfile for the server

It mirrors the Dockerfile Wasp generates in .wasp/out, with one extra stage: the Wasp CLI is installed and wasp install && wasp build run inside the image, so the repository stays source-only. Debian images rather than Alpine, because the Wasp CLI ships glibc binaries for linux/arm64 and linux/x64.

ARG WASP_VERSION=0.25.0
ARG NODE_IMAGE=node:24-bookworm-slim

FROM ${NODE_IMAGE} AS base
RUN apt-get update \
 && apt-get install -y --no-install-recommends openssl ca-certificates \
 && rm -rf /var/lib/apt/lists/*

FROM base AS builder
ARG WASP_VERSION
RUN apt-get update \
 && apt-get install -y --no-install-recommends python3 build-essential \
 && rm -rf /var/lib/apt/lists/* \
 && npm install -g @wasp.sh/wasp-cli@${WASP_VERSION}
WORKDIR /app
COPY . .
RUN wasp install && wasp build
RUN cd .wasp/out/server \
 && npm install \
 && npx prisma generate --schema=../db/schema.prisma \
 && npm run bundle \
 && mkdir -p node_modules

FROM base AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/.wasp/out/sdk ./.wasp/out/sdk
COPY --from=builder /app/.wasp/out/server/node_modules ./.wasp/out/server/node_modules
COPY --from=builder /app/.wasp/out/server/bundle ./.wasp/out/server/bundle
COPY --from=builder /app/.wasp/out/server/package*.json ./.wasp/out/server/
COPY --from=builder /app/.wasp/out/db ./.wasp/out/db
WORKDIR /app/.wasp/out/server
EXPOSE 3001
ENTRYPOINT ["npm", "run", "start-production"]

Add a .dockerignore with node_modules, .wasp, .git and .env.* so the build context is the source only. start-production runs prisma migrate deploy and then the server, exactly as Wasp's generated image does.

2.Create the PostgreSQL add-on

Services → Add database → PostgreSQL. The $1 DB Sprint Pack gives a full Mini-tier database for 12 hours with a 24-hour window to keep it; the Mini tier is $29 a month. Once the add-on is linked to the server container, DATABASE_URL is injected into its environment; you can also paste the connection string from the add-on's credentials page as a variable yourself.

3.Deploy the server from GitHub

Deploy Your Application → GitHub → the repository and branch. The Dockerfile is detected; port 3001 comes from its EXPOSE line and SnapDeploy injects PORT=3001, which the Wasp server reads. Set these variables before the first build:

VariableValue
DATABASE_URLinjected by the linked add-on, or pasted from its credentials
JWT_SECRETa random string of at least 32 characters
WASP_SERVER_URLhttps://<server-name>.containers.snapdeploy.app (the container name you choose)
WASP_WEB_CLIENT_URLthe client URL from step 5; any placeholder for the first build

The build installs Wasp, compiles the app and bundles the server; expect a few minutes the first time. When the image is pushed the container starts, applies the migrations and answers at its URL. The health check accepts the server's root path, so no extra route is needed. If your app uses an OAuth provider or an email sender, add those variables here too.

Deploy the Wasp Client

The client is static files built by Vite. A second Dockerfile in the same repository builds them and serves them with nginx, with the single-page-app fallback that client-side routes like /login need.

4.Add client.Dockerfile and nginx.conf

ARG WASP_VERSION=0.25.0
ARG NODE_IMAGE=node:24-bookworm-slim

FROM ${NODE_IMAGE} AS builder
ARG WASP_VERSION
ARG REACT_APP_API_URL
RUN apt-get update \
 && apt-get install -y --no-install-recommends openssl ca-certificates python3 build-essential \
 && rm -rf /var/lib/apt/lists/* \
 && npm install -g @wasp.sh/wasp-cli@${WASP_VERSION}
WORKDIR /app
COPY . .
RUN wasp install && wasp build
RUN test -n "$REACT_APP_API_URL" || (echo "REACT_APP_API_URL is not set" && exit 1)
RUN REACT_APP_API_URL=${REACT_APP_API_URL} npx vite build

FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
RUN rm -rf /usr/share/nginx/html/*
COPY --from=builder /app/.wasp/out/web-app/build /usr/share/nginx/html
EXPOSE 80

Wasp's Vite build emits 200.html as the entry page, not index.html, and COPY merges into the nginx image's html directory, so the stock welcome page is removed first and nginx is told to serve 200.html:

server {
    listen       80;
    server_name  _;
    root         /usr/share/nginx/html;
    index        200.html;
    location /assets/ {
        add_header Cache-Control "public, max-age=31536000, immutable";
        try_files $uri =404;
    }
    location / {
        try_files $uri $uri/ /200.html;
    }
}

5.Deploy the client from the same repository

Deploy Your Application → GitHub → the same repository → Advanced Options → Dockerfile Path client.Dockerfile. Add one variable before the first build:

VariableValue
REACT_APP_API_URLthe server URL from step 3

Vite bakes REACT_APP_API_URL into the compiled JavaScript. SnapDeploy passes REACT_APP_, VITE_ and NEXT_PUBLIC_ variables into the Docker build as build arguments, which is why the Dockerfile declares it as ARG and refuses to build without it. If you change it later, redeploy; a restart is not enough.

6.Point the server at the client and redeploy

On the server container set WASP_WEB_CLIENT_URL to the client's URL and redeploy. Open the client URL, sign up, create something. From now on every push to the branch rebuilds both containers; new Prisma migrations committed with your code are applied by the server on its next start.

What the Free Tier Gives a Wasp App, and Where It Stops

The two containers follow the same rules as any other; the complete list is on the free-tier page.

  • Two of the four free containers, 512 MB and 0.25 vCPU each, no credit card. The server idles well under 100 MB; the client is nginx.
  • 100 container-hours a month, counted only while a container is awake, shared by both.
  • Sleep after 15 idle minutes. A browser visit to the client wakes the client, but the client's API calls do not wake a sleeping server: the wake page only runs in a browser. For a demo, open the server URL once in a tab; for anything people depend on, put the server on Always-On ($12 a month) and leave the client free.
  • 10 deploys a day (5 per rolling 12 hours), failed builds included, and each push rebuilds both containers, so one push counts twice.
  • The database never sleeps and is not free: $1 for 12 hours to try, $29 a month for Mini (1 GB RAM, 5 GB storage, 50 connections).
  • Custom domains for the client and server need Always-On; the certificate is issued for you.

Wasp Hosting Questions, Answered

Do I have to run wasp build before deploying to SnapDeploy?

No. The server Dockerfile on this page installs the Wasp CLI and runs wasp install and wasp build inside the build, so the repository holds your app's source and every push to the branch rebuilds the container. You never commit the .wasp/out output.

Why two containers?

A built Wasp app is a Node.js API server plus a static React client, and Wasp's own docs deploy them as two things with two URLs. On SnapDeploy they are two containers from the same repository: the server from Dockerfile and the client from client.Dockerfile, chosen with the Dockerfile Path field under Advanced Options. Both fit in the free tier's 4 containers.

Where does the PostgreSQL database come from?

From a managed PostgreSQL add-on next to the containers: the $1 DB Sprint Pack runs for 12 hours to try things, the Mini tier is $29 a month. Link it to the server container and DATABASE_URL is injected into the environment; the server runs prisma migrate deploy on every start, so committed migrations are applied automatically.

How does the client know the server's URL?

Through REACT_APP_API_URL, which Vite bakes into the JavaScript at build time. Add it as an environment variable on the client container before the first build: SnapDeploy passes REACT_APP_, VITE_ and NEXT_PUBLIC_ variables into the Docker build as build arguments. Changing it later needs a redeploy, not a restart.

Does the Wasp server need a health route?

No. The platform probes the common health paths and then the root path before routing traffic, and the Wasp 0.25 server answers GET / with 200, so the deploy passes without any change to the app.

Can a Wasp app run on the free tier?

Yes, as a demo: two of the four free containers, 100 container-hours a month shared, no credit card. Both containers sleep after 15 idle minutes. A browser visit wakes the client, but the client's API calls do not wake a sleeping server, so for anything people depend on run the server on Always-On at $12 a month; the client can stay free.

Which Wasp version does this cover?

Wasp 0.25, the current release on 6 October 2026, with Node.js 24 in the build. The Wasp version is an argument at the top of each Dockerfile; bump it when you upgrade the app.

Proud Member Of

As Seen On

Put Your Wasp App Online From Its Repository

Two Dockerfiles, a PostgreSQL add-on, no credit card for the containers. Always-On from $12 a month when the server has to stay up.

Deploy Free Today
FIRST OF ITS KIND

Sprint Pack — $1 for 24 hours

The first managed container PaaS to offer a one-time 24-hour Always-On pass under $5. Unlimited deploys + Always-On for one Small (512 MB) container — including WebSockets & real-time apps. No subscription, no auto-renewal. Stack two for 48 hours.

Need a managed database too? DB Sprint Pack — $1 / 12h for Postgres, MySQL, MariaDB, Mongo, Redis, or RabbitMQ.