opensaas-sh/blog/src/content/docs/guides/deploying.mdx
import stripeListenEvents from '@assets/stripe/listen-to-stripe-events.png'; import npmVersion from '@assets/stripe/npm-version.png'; import stripeSigningSecret from '@assets/stripe/stripe-webhook-signing-secret.png'; import { Image } from 'astro:assets';
Because this SaaS app is a React/NodeJS/Postgres app built on top of Wasp, Open SaaS can take advantage of Wasp's easy, one-command deploy or manual deploy to any provider of your choice.
The simplest and quickest option is to take advantage of Wasp's one-command deploy to Fly.io or Railway.
Or if you prefer to deploy to a different provider, or your frontend and backend separately, you can follow the Deploying Manually section below.
These are the steps necessary for you to deploy your app. We recommend you follow these steps in order.
Each of these steps is covered in more detail below.
If you're storing files in AWS S3, ensure you've listed your production domain
in the bucket's CORS configuration under AllowedOrigins. Check the File
uploading guide for details.
Make sure you've got all your API keys and environment variables set up before you deploy.
In the Payments Processor integration guide, you set up your API keys using test keys and test product ids. You'll need to get the live/production versions of those keys. To get these, repeat the instructions in the Integration Guide without being in test mode. Add the new keys to your deployed environment secrets.
Many of your other environment variables will probably be the same as in development, but you should double-check that they are set correctly for production.
Here are a list of all of them (some of which you may not be using, e.g. Analytics, Social Auth) in case you need to check:
DATABASE_URLJWT_SECRETWASP_WEB_CLIENT_URLWASP_SERVER_URLOPENAI_API_KEYSENDGRID_API_KEYGOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRETGITHUB_CLIENT_IDGITHUB_CLIENT_SECRETREACT_APP_PLAUSIBLE_ANALYTICS_ID (for client-side)PLAUSIBLE_API_KEYPLAUSIBLE_SITE_IDPLAUSIBLE_BASE_URLREACT_APP_GOOGLE_ANALYTICS_ID (for client-side)GOOGLE_ANALYTICS_CLIENT_EMAILGOOGLE_ANALYTICS_PROPERTY_IDGOOGLE_ANALYTICS_PRIVATE_KEY
(Make sure you convert the private key within the JSON file to base64 first with echo -n "PRIVATE_KEY" | base64. See the Analytics docs for more info)AWS_S3_IAM_ACCESS_KEYAWS_S3_IAM_SECRET_KEYAWS_S3_FILES_BUCKETAWS_S3_REGION:::tip[Deployed? Get some swag! 👕]
Do you have an Open SaaS app running in production? If yes, we'd love to send some swag your way! All you need to do is fill out this form and we'll make it happen.
:::
After deploying your server, you need to add the correct redirect URIs to the credential settings. For this, refer to the guides from the Wasp Docs.
Now you need to set up your stripe webhook for production use. Below are some important steps and considerations you should take as you prepare to deploy your app to production.
When you create your Stripe account, Stripe will automatically assign you to their latest API version at that time. This API version is important because it determines the structure of the responses Stripe sends to your webhook, as well as the structure it expects of the requests you make toward the Stripe API.
Because this template was built with a specific version of the Stripe API in mind, it could be that your Stripe account is set to a different API version.
:::note
const STRIPE_API_VERSION = "2025-04-30.basil"; // or e.g. 2023-08-16 if using older format
export const stripeClient = new Stripe(env.STRIPE_API_KEY, {
apiVersion: STRIPE_API_VERSION,
});
When you specify a specific API version in your Stripe client, the requests you send to Stripe from your server, along with their responses, will match that API version. On the other hand, Stripe will send all other events to your webhook that didn't originate as a request sent from your server, like those made after a user completes a payment on checkout, using the default API version of the API.
This is why it's important to make sure your Stripe client version also matches the API version in your Stripe account, and to thoroughly test any changes you make to your Stripe client before deploying to production. :::
To make sure your app is consistent with your Stripe account, here are some steps you can follow:
default API version in the Stripe dashboard under the Developers section./src/payment/stripe/stripeClient.ts file matches the default API version in your dashboard:
const STRIPE_API_VERSION = "2025-04-30.basil"; // or e.g. 2023-08-16 if using older format
export const stripeClient = new Stripe(env.STRIPE_API_KEY, {
apiVersion: STRIPE_API_VERSION,
});
package.json to match the API version in your dashboard:Published date column until you find the package release that matches your version. For example, here we find the NPM version 13.x.x matches the API version of 2023-08-16.
<Image src={npmVersion} alt="npm version" loading="lazy" /> npm install [email protected] # e.g. npm install [email protected]
+ add endpoint/payments-webhook, e.g. https://open-saas-wasp-sh-server.fly.dev/payments-webhook
<Image src={stripeListenEvents} alt="listen events" loading="lazy" />src/payment/stripe/webhook.ts:
<Image src={stripeSigningSecret} alt="signing secret" loading="lazy" />reveal the new signing secret.STRIPE_WEBHOOK_SECRET environment variable. If you've deployed to Fly.io, you can do that easily with the following command:
wasp deploy fly cmd --context server secrets set STRIPE_WEBHOOK_SECRET=whsec_...
To set up your Lemon Squeezy webhook, you'll need the URL of you newly deployed server + /payments-webhook, e.g. https://open-saas-wasp-sh-server.fly.dev/payments-webhook.
With the webhook url ready, go to your Lemon Squeezy Webhooks Dashboard:
+ button.Callback URL section.LEMONSQUEEZY_WEBHOOK_SECRET.order_createdsubscription_createdsubscription_updatedsubscription_cancelledsave.Make sure to turn off the sandbox mode in your server's production environemnt:
POLAR_SANDBOX_MODE=false
To set up your Polar webhook, you'll need the URL of your newly deployed server + /payments-webhook, e.g. https://open-saas-wasp-sh-server.fly.dev/payments-webhook.
With the webhook URL ready, in your Polar Dashboard:
Settings > Webhooks.Add Endpoint button.Format to "Raw".order.paidsubscription.updatedSave.POLAR_WEBHOOK_SECRET environment variable in production.Fly.io is a platform for running your apps globally. It's a great choice for deploying your SaaS app because it's free to get started, can host your entire full-stack app in one place, scales well, and has one-command deploy integration with Wasp.
Wasp provides the handy wasp deploy command to deploy your entire full-stack app (DB, server, and client) in one command.
To learn how, please follow the detailed guide for deploying to Fly via the Wasp CLI from the Wasp documentation. We suggest you follow this guide carefully to get your app deployed.
:::caution[Setting Environment Variables]
Remember, because we've set certain client-side env variables, make sure to pass them to the wasp deploy commands so that they can be included in the build:
REACT_APP_CLIENT_ENV_VAR_1=<...> REACT_APP_CLIENT_ENV_VAR_2=<...> wasp deploy
The wasp deploy command will also take care of setting the following server-side environment variables for you so you don't have to:
DATABASE_URLPORTJWT_SECRETWASP_WEB_CLIENT_URLWASP_SERVER_URLFor setting the remaining server-side environment variables, please refer to the Deploying with the Wasp CLI Guide. :::
If you prefer to deploy manually, your frontend and backend separately, or just prefer using your favorite provider you can follow Wasp's Manual Deployment Guide.
:::caution[Client-side Environment Variables]
Remember to always set additional client-side environment variables, such as REACT_APP_STRIPE_CUSTOMER_PORTAL by appending them to the build command, e.g.
REACT_APP_CLIENT_ENV_VAR_1=<...> npm run build
:::
Deploying your Astro Starlight blog is a bit different than deploying your SaaS app. As an example, we will show you how to deploy your blog for free to Netlify. You will need a Netlify account and Netlify CLI installed to follow these instructions.
Make sure you are logged in with Netlify CLI:
netlify status.netlify login.Position yourself in the blog directory and run the following command:
npm run build
This will build your blog into the blog/dist directory. Now you can deploy your blog to Netlify with the following command:
netlify deploy
Select the dist directory as the deploy path.
Finally, if the deployment looks good, you can deploy your blog to production with the following command:
netlify deploy --prod