Skip to content

Blog

Here's my SaaS Cost, Profit, and Marketing Breakdown

Hey builders,

I wanted to share my journey building a micro SaaS, CoverLetterGPT, which earns $550/month in recurring revenue (MRR), while requiring minimal effort and maintenance. Here’s a breakdown of overall costs, profit, how I got customers, and why I believe small, simple SaaS apps are an underrated way to start as an indie maker.

At a glance

CoverLetterGPT is a GPT wrapper that generates personalized cover letters based on the user’s uploaded CV and job description. The function that seperates it from just using chatGPT is that users can edit cover letters inline with AI assistance, as well as manage all the different cover letters they’ve generated. It’s super simple, and it’s even open-source! I launched it in August 2023 and it’s been a steady source of passive income since.

Here are some quick numbers:

  • Built in 1 week
    • using Open SaaS, a free, open-source React, NodeJS, SaaS boilerplate template with tons of features.
  • Runs on autopilot
    • ~1hr/month of maintenance
  • ~$550 MRR
  • Minimal customer support
    • Only 3 Stripe disputes to date
  • Costs ~$16/month
    • ~$12 for hosting
    • ~$3 for OpenAI API fees
    • $11.82/year for the .xyz domain
  • $0 paid ads
    • Just SEO and Social Media/Reddit
CoverLetterGPT MRR & Revenue Chart

Costs Breakdown

Cost TypeMonthly CostNotes
App Hosting$12Railway.com
OpenAI API$3~1,500 requests / 1.5m tokens
Domain$0.98$11.82/year via Porkbun
Stripe Fees~$45~3% + 30¢/transaction + ~2% currency conversion

As you can see, Stripe fees are the biggest “cost” in the end, becuase most of my customers are abroad, so I get hit with extra cross-border fees. But that’s just the cost of doing business. Plus, I don’t really see these fees, so they dont bother me much.

Meanwhile, using the OpenAI API isn’t as expensive as you might think, and the models keep getting cheaper and better. I’m currently using the gpt-4o-mini model for the standard plan, and gpt-4.1 for the pro plan.

CoverLetterGPT SubscriptionPricing

At about 1,500 requests/month, which equals roughly 1.5m input and output tokens, this costs me around $3/month.

CoverLetterGPT OpenAI Cost CoverLetterGPT OpenAI Requests

Besides that, my hosting bill is about $12/month on Railway, and I pay $11.82/year for the .xyz domain via Porkbun.

Revenue & Profit Breakdown

MetricValueNotes
Avg. Monthly Revenue$615Past 8 months, converted from €543
Total Net Revenue$9,912Since launch, after Stripe fees
Total Costs to Date$345$15/month × 23 months
Avg. Monthly Profit$416$9,567 ÷ 23 months
Total Profit to Date 🎉$9,567Net revenue minus costs

At current exchange rates, my average monthly recurring revenue of €543 over the past 8 months equals

  • an average of $615 MRR for the past 8 months

My total revenue, minus ~$45/month Stripe fees, disputes, and refunds, since launch has been €8756. At current exchange rates, this equals

  • $9,912 total net revenue

My costs since launch 23 months ago have been ~$15/month. So 15 * 23 equals

  • $345 monthly costs

This brings my total profit since launch to

  • $9,567 total profit or
  • $416/month average profit

That’s enough to afford a lease on a nice Jeep Wrangler.

cars I can lease for $416/month

Not bad for a side project that I built in 1 week!

Marketing Breakdown

ChannelEffort LevelReturnResult / Notes
Product HuntHighMediumInitial launch, created marketing assets, got early traction
RedditMedium-HighHighPosted in dev/entrepreneur/job subreddits, boosted SEO, some bans risk
Indie HackersEasyMediumShared open-source story, featured in newsletter, good feedback
TwitterHardLowShared updates, videos, and journey.
TikTokMedium (Ongoing)HighShared updates, videos, and journey, helps maintain MRR.
Paid AdsNoneDid not run any paid ads

By far the question I get asked the most by other curious builders is how I got customers. Many ask if I paid for ads, and I didn’t.

Here’s what I did do:

Initial Product Hunt Launch

A lot of people aren’t sure if Product Hunt is worth it these days (spoiler: it still is!), but I’d say launch there anyway.

CoverLetterGPT Product Hunt Launch

I wasn’t sure where to go with my app after I first built it, so Product Hunt seemed like a good start. The benefit of launching there, regardless of how the launch performed on the platform, was that it forced me to do a few important things.

First, I had to create good marketing materials for the launch, like videos, images, and marketing copy.

Then, with the launch coming up, I felt that I had to start telling people about it. I mean, there’s no point in launching and then not trying to get some upvotes and support, right?

This all lead to giving me a jump start on spreading the word about the app. It did decently well on product hunt, and gained some initial traction, but with that material I also went to other platforms to share it.

Reddit Posts and Comments

CoverLetterGPT Reddit Post

Posting on Reddit can be tricky. If you’re too forward, it gets seen as spam and you can get banned.

Luckily, I left the app open-source because I wasn’t anticipating much success from it, and a good side effect of this was that I was able to openly post about it on developer and entrepreneur subreddits. To this day, I think this had a good effect on SEO, but I’m not certain.

Besides that, I found job search subreddits and left comments on posts where people were asking about using AI to generate cover letters.

Indie Hackers

I also posted about it in the IndieHackers community and got great feedback, mainly due to the fact that it was open-source and starting to get good signup numbers.

CoverLetterGPT Indie Hackers

This led to a getting featured in their newsletter—again, thanks to the app being open-source—and this led to a lot more buzz.

Twitter & tiktok

I also took to twitter and tiktok to share fun videos about the app, my journey building it, and my thoughts about it.

Doing this periodically and consistently throughout the past couple years has probably helped keep MRR consistent

What I’ve learned

This experience has been pretty interesting. I’ve defintiely had a bit of luck and good timing on my side, but I’ve also learned a lot about building a SaaS, marketing, and what tends to be good and bad advice out there.

So here’s my take on things.

Small, Persistent Wins Are Worth It

Many developers think a SaaS has to be big, flashy, or wildly profitable to be worth building. I disagree. For me:

  • $550/month is fantastic as side income.
  • Revenue has been surprisingly stable.
  • It runs itself, requiring virtually no maintenance.
  • I can balance it easily alongside my full-time job.
  • It’s fun and doesn’t consume my free time.

I would encourage anyone who wants to build a SaaS to go for it, but to aim for small, achievable SaaS projects instead of trying to “hit it big” from the start.

Build & Launch Fast

The most important lesson I’ve learned: speed is everything. The faster you launch, the faster you’ll know if your idea works. Here’s what worked for me:

  1. Avoid long, drawn-out failures: Build small, execute early.
  2. Use the fastest tools available: I used Open SaaS because it gives me all the building blocks already set up (auth, Stripe payments, OpenAI API examples, email sending, etc), letting me focus on the business logic of the app.
  3. Forget perfection: I didn’t worry about making it pretty or perfect—-it just had to work.

Keep It Simple

The beauty of a simple, “micro SaaS” is in its simplicity. Here’s why:

  • My app does one thing well: generating cover letters based on résumés and job descriptions and allows users to edit them inline with AI assistance.
  • There’s no need for a fancy landing page or marketing gimmicks. This is my 🌶 hot take. I mean, my landing page is my app! Users land on it and can instantly try it out.
  • Users get 3 trial credits-—enough to try the app and see value before paying.
CoverLetterGPT landing page

One of the biggest perks of small SaaS is how low-maintenance it can be. With CoverLetterGPT, I rarely handle customer service thanks to its simplicity and the low, but consistent number of users (~100).

This means I spend my time on new ideas rather than maintaining old ones.

It’s All About Tradeoffs

While I could optimize and grow CoverLetterGPT further, I’ve chosen to keep it small and simple. For me:

  • Small wins are still wins.
  • I value having a side project that’s easy to manage alongside my full-time job.
  • I’d rather have less stress than chase higher profits.

Final Thoughts

If you’re considering building a SaaS, don’t overthink it. Start small, move fast, and treat it as an experiment. Forget the “rules” and focus on launching. Here’s what matters most:

  • Keep it simple: Build an app that solves one problem well.
  • Launch fast: Test your idea and iterate based on real feedback.
  • Minimize effort: Aim for maximum reward with minimal maintenance. If you’re spending months on it before people can try it, you’re probably working on the wrong initial idea.

For me, $550 MRR isn’t just “enough”—it’s amazing. It’s proof that small, focused apps can succeed, and they’re a great way to build confidence and skills as a maker.

Product Hunt doesn't really work, but you should still use it to launch your product

Many of us have been launching on Product Hunt for a while, and more and more folks have started questioning whether the audience there is genuine and whether it is still worth launching on their platform.

Being fresh out of our latest launch from a week ago, I wanted to share here our first-hand experience and cover three main things:

  • How does launching on Product Hunt look and feel today
  • What we got from the launch
  • How to make (the best) use of Product Hunt for your product

About us - launched 6x, >2,000 upvotes

We’ve launched 6 times on Product Hunt in the last 4 years, won “Top Product” awards (#1 and #5 of the day), and collected over 2,000 upvotes in total. Our last launch was with Open SaaS - an open-source alternative to $300+ SaaS starters.

Make apps for everyone

You will find many articles with advice for launching on PH, and winning stories from those who got featured, but almost nobody shares behind-the-scenes knowledge and what it really took to get there. That is the purpose of this post.

I will guide you through the steps of the launch and comment and share our experiences from each of them. Let’s get started:

Scheduling your launch and creating a “Coming soon” teaser - “let’s exchange upvotes”

Make apps for everyone

Once you schedule your Product Hunt launch, you can create a banner to appear on their “Coming soon” page (https://www.producthunt.com/coming-soon), and this is where your journey starts. This gives PH visitors an opportunity to see what’s coming next and to subscribe to get notified once it launches, and it is also the first thing you can use for marketing your launch.

This is also when the PH economy starts - as soon as you publish your launch teaser, you will start receiving offers to exchange upvotes with other people launching their products soon:

Make apps for everyone

This is actually a legitimate strategy (in the sense of shared incentives and not buying votes) that can probably be utilized pretty efficiently via automation. It won’t bring any qualified leads (aka people genuinely interested in your product), but it might help with the upvotes, resulting in the increased visibility and reach of your launch.

We haven’t used this strategy at all (so I cannot testify to its efficiency), since we published our “Coming soon” page quite late, just a day or two before the launch, and we also didn’t have the workflow in place nor manpower to pull it off.

There are also specialized groups on Linkedin, WhatsApp, and other platforms for PH participants to support each other in this way. If you join these, you will, expectedly so, receive even more such messages and requests.

Launch day - unsolicited emails and “buy upvotes” offers

On the launch day, the requests like the one above intensified. I even got several emails from others launching products on the same day asking for an upvote, as they scraped my email and added me to their newsletter.

First 4 hours of the day - hidden upvotes

Product Hunt recently introduced the feature of showing the products in the randomized order, with the upvote count hidden, for the first 4 hours of the day. The idea behind this is to guarantee all products have equal visibility at the start and a fair chance to grab the attention of the audience.

With our latest launch, Open SaaS, we had the best opening ever - 100 upvotes in 4 hours!

Make apps for everyone

We, of course, engaged our network, but also noticed a lot of upvotes and comments from the people we don’t personally know. With such a strong start, I was quite confident we secured our place in the top 5 products on the leaderboard.

Being in the top 5 products is an “above the fold” position on Product Hunt’s home page, so getting there early is the best way to end up there.

But when the leaderboard was finally revealed, Open SaaS was barely in the top 10 launches of the day!

Make apps for everyone

There was a quite noticeable cut-off between the first five places and the rest, and the product in the first place had almost double the upvotes than the second one. That was fairly demotivating for us as it felt like we had literally zero chance of catching up.

”Hey, wanna buy some upvotes?”

After the leaderboard reveal, we started receiving another type of message - direct offers to buy upvotes. Being still relatively close to being in the top 5 products probably made us a highly qualified lead:

Make apps for everyone

A slight variation of this is having different social media influencers and community owners reach out and offer to market your launch to their followers, promising X upvotes:

Make apps for everyone
Make apps for everyone

Even some of our direct contacts knew “a guy” that could get you to the top of Product Hunt and offered to intro us, so it kinda started feeling like a “public secret”, and us being the rare ones who didn’t know about it.

Make apps for everyone

The main benefit of our PH launch wasn’t the launch itself, but rather the fact we could combine it with other things, like launching Open SaaS on HackerNews, where it ended up being featured for about half a day (and much longer on Show HN tab).

Make apps for everyone

Finally, all that engagement combined allowed us to get trending globally on GitHub, which in turn brought in even more traffic to Open SaaS (today, a week after launching, it has over 2.5k stars).

Make apps for everyone

The resulting traffic

Taking a look at the traffic that was brought to Open SaaS’s repo in the last two weeks, here’s what we can observe:

Make apps for everyone
Make apps for everyone

HackerNews launch brought in more than 3 times more people than Product Hunt. GitHub brought even fewer people to the actual repo, but my gut feeling is that many more of them starred it without leaving the Trending page.

Make apps for everyone

Open SaaS ended the launch as the #7 product of the day, with about ~400 upvotes. The top 10 products of the day end up in a daily newsletter that has over 500,000 subscribers, according to the Product Hunt.

The newsletter starts with 3 big promotional blocks, so you must scroll quite a bit to reach the top products of the previous day.

Make apps for everyone

For us, it didn’t make a huge dent, I think it got us about 20 upvotes. Maybe it was due to the fact we weren’t number one, or simply because it’s quite a deep funnel (open email → scroll all the way down → check all the products → like Open SaaS → decide to upvote it).

Make apps for everyone

Is it even possible to win #1 of the day without any boosting strategies?

Yes, it is definitely still possible. I’ve had it confirmed by a couple of contacts that I trust and who won #1 with their products, without any bots or paying for upvotes. But it’s also definitely become less common and less predictable.

Most of the times we were launching, the product in the first place exhibited some unusual behavior. Once, it was the company that launched the week before, but they just slightly rebranded the product and the website and re-launched it. Another time, a product received a very sudden spike in upvotes just hours before the end of the launch.

So, what does all this mean? Is it still worth launching on Product Hunt?

It is obvious that today, there are different forces and incentives driving the behavior of Product Hunt users from both sides. Initially, there was a community that wanted to learn about the latest products and express their interest, and there were founders that wanted to connect to that community.

Now, there are also creators who foremost want their product to win, no matter the actual audience engagement, as they believe that will help them with their end goal - e.g., reach, fundraising, or social validation towards other users. And there is obviously a side willing to fulfill that demand without having any real interest in the product.

Why is that possible? Product Hunt is taking a lot of measures to detect and prevent such behavior, but it is hard to do it without severely limiting the network effect (aka being able to share your launch link around) that Product Hunt is going for.

Besides all that, for us it is still worth it to regularly launch on Product Hunt. Here’s why.

Product Hunt is an amazing excuse

Product Hunt gives you a unique opportunity to declare an “official” launch of your product. You can decide on which day you want to do it, schedule it, and it’s 100% going to be there for everybody to see, and for you to share and invite people to check it out. You get 24 hours, during which it is fully justifiable to contact everyone you know (and beyond) and keep tooting your horn.

Make apps for everyone

You can’t do that with other high-reach platforms such as Reddit, and HackerNews. You are, of course, free to share the news about your product at any time, but there is no guarantee that anybody will see it (quite the opposite, actually) unless the collective mind of the community decides so, which is all but deterministic. You could easily spend a week preparing your launch post just for it to get drowned by the algorithm in minutes.

That’s why we look at Product Hunt not as the final goal (winning #1) but as simply a part of our overall launch process. It’s a great podium to be standing on, and a good excuse to talk about your product, and anything else on top is just a bonus.

We keep it simple

You will find a lot of articles (and paid courses) from “PH gurus”, explaining how you should prepare your launch months in advance, warm up your audience, prepare comments they will share, etc. We don’t do any of that. We just prepare the content (video + a few screenshots, and an intro comment), and, on the day of the launch, invite everyone we know to support us. Then, during the day, we also post on Reddit, Hackernews, and dev.to.

Make apps for everyone

Sometimes we end up in the top 5, sometimes we don’t, but every time, we get a solid uptick in user engagement, and usually something much better follows in the next days/weeks. For example, MAGE, our GPT-powered full-stack app starter, exploded after its PH launch and has been used to create over 30,000 projects in a few months.

We do it often

Our goal is to launch on Product Hunt every 3 months as a part of our Launch Week, and that’s what we’ve done so far. You cannot really launch the exact same product unless 6 months have passed or there’s been a significant update, but you are free to launch other (sub)products and features connected to your main product.

💡 Hint: when you submit a launch, you can ask the PH team to “connect” it to your product so it will appear in a list of launches for that product. Often, they do it on their own. It will look like this:

Make apps for everyone

Although our main product is Wasp, a full-stack framework on top of React & Node.js, here’s what we launched so far:

It’s become a regular part of our launch workflow, and for whatever new feature(s) we introduce in that quarter, we’ll look for a good candidate to showcase in the upcoming launch. That allows us to keep talking about what we do, and we also get a lot of good content (e.g., videos, banners) that we can embed in our docs, blog posts, etc.

For example, this is a video showing how auth works in Wasp - first we used it for our Product Hunt launch, and now it lives at the top of our auth docs.

Thanks for reading!

Thanks for reading this far! This turned out to be quite a bit longer post than I initially expected, but I just kept getting more ideas on what to write about. I hope you will find it helpful for planning your next launch and that you will also know a bit better what to expect along the way.

I would also love to get your feedback and hear about your experiences and strategies for launching on Product Hunt.

Happy launching!

Going from an Idea to MVP in Weeks: PromptPanda's Launch(es)

Did you know that most co-founders meet each other through work? Lander Willem met his friend and co-founder Bram Billiet while they were working at the local venture fund. They both shared the love towards LLMs and got the idea to kickstart their SaaS after experiencing the same pain points with managing and versioning prompts.

In this post, you’ll learn how they:

  • Shipped their SaaS from idea to MVP in weeks, using modern AI stack
  • Launched and got trending on Product Hunt with 100+ upvotes
  • Successfully onboarded first users

The problem: Managing prompts is messy

Right after OpenAI released their first LLM models, Lander and Bram started exchanging tips on how to get optimal results from prompts. Soon, they learned that managing AI prompts is often chaotic.

People who share prompts usually do so through messaging apps such as Slack, Microsoft Teams or in better cases, shared Google Docs documents. Some of the people they talked to even confessed they were sharing their favorite prompts using screenshots 😅. Although a Google Doc might work initially, people quickly bump into issues regarding versioning and granular access management.

This is how they got the idea to create PromptPanda - a SaaS that allows people to exchange prompts in an easy way. Here’s an interactive demo you can click through to see what they’ve built:

The opportunity: Everyone uses prompts, not just devs

Other AI prompt tools are primarily designed with developers in mind, which leaves out non-technical teams. Those less technical users depend heavily on collaboration, efficiency, and consistency to complete their tasks. This is the market PromptPanda decided to go after.

Make apps for everyone

The tool is designed specifically to help teams centralize their prompts and ensures consistent output quality. Collaboration is painless because of an intuitive web app that also has a Chrome extension.

PromptPanda integrates with major AI providers such as OpenAI, Anthropic, Google, Perplexity, and DeepSeek. Coupled with its built-in Prompt Improver, these integrations allow users to quickly test, iterate, and enhance their prompts, while not imposing any limitations for the end-users.

PromptPanda interface

With this approach they covered a market that other companies overlooked, non-technical users who rely on the biggest LLM providers for their daily tasks.

⭐️ Star the Open SaaS repo and support open-source tools for builders, by builders!

Launching is unpredictable: Product Hunt hits and flops

As soon as the app was somewhat stable and usable, Lander and Bram decided to launch on ProductHunt.

PromptPanda on Product Hunt

Their first ProductHunt launch was great in terms of visibility. They were featured by the ProductHunt team which got them a bunch of upvotes and comments. Although there was quite a lot of engagement with the launch, it didn’t really end up in sticky, paying customers.

PromptPanda on Product Hunt

A short while later they relaunched on ProductHunt after processing the feedback from their first launch. Both their product and launch campaign were much better prepared. Weirdly enough, the launch mostly failed as they got almost no upvotes or conversions.

Trying again

Although their second launch was mostly a flop, it did manage to get them mentioned in a Superhuman (the email app) newsletter. Their user base doubled overnight.

Ever since then they have an active stream of users and new signups coming in.

”My main takeaway is to never stop shipping, and always share your work!”

Lander Willem

Most of their users today have found PromptPanda through organic SEO. They started writing articles about AI Prompt Management which have quickly found traction in search engine algorithms.

Choosing the right stack for developing your SaaS app

PromptPanda’s team chose Open SaaS because it significantly streamlined their product development by simplifying backend setup, database management, and built-in authentication. This was crucial as they needed an efficient solution that could save time due to their busy schedules. Wasp’s default integration with Fly also enabled rapid deployment, allowing them to quickly validate their product idea without getting bogged down in infrastructure complexities.

Here’s a full overview of their tech stack alongside all the tools they rely on to run their SaaS:

PromptPanda tech stack

Are you ready to ship your SaaS now?

PromptPanda’s story proves the best SaaS ideas come from solving your own pain points. Lander and Bram also learned launching isn’t predictable—success can come from unexpected places, even failed launches. The takeaway? Keep building, keep shipping, and always share your progress openly.

If you enjoyed this post please make sure to give Open SaaS a star on GitHub, this keeps us going forward and supports our work!

Meet Marko Saric, Co-founder of Privacy-friendly Plausible Analytics

In this interview, Marko Saric shared his thoughts on privacy and running a bootstrapped SaaS business. Plausible integration is already available in Open SaaS as a privacy-friendly alternative to Google Analytics. We hope this interview helps you understand the value of such a product, and the nature of running an open source business.

Here’s a few other things we’ve covered in this interview:

  • Tackling big tech privacy issues.
  • How bootstrapping your business fuels independence and transparency.
  • Real, practical advice for growing your SaaS the smart way.

Let’s dive in!

Can you share a bit about your background and what led you to start Plausible?

I’m Marko Saric, co-founder of Plausible Analytics.

My journey with Plausible began with a growing awareness of the privacy issues surrounding Google and its products. For many years, I was a user of Google’s services but over time (and thanks to Snowden, Cambridge Analytica and other privacy scandals), I became more aware of the negative aspects of surveillance capitalism. This led me to explore better, more ethical alternatives to the big tech products.

I started sharing these alternatives on my blog which is how I connected with my co-founder Uku. We both had experience in tech and a shared vision of working on a privacy-friendly analytics tool so we decided to work together on Plausible. I’m focused on marketing and communication side of things while Uku is focused on design and development.

For those unfamiliar with Plausible, how would you describe its core mission in just a few sentences?

Plausible Analytics is an easy to use, lightweight, open source and privacy-friendly analytics tool. Our mission is to provide website owners with useful insights while respecting visitor privacy.

We have been working on Plausible for more than 6 years now, have more than 14,000 active subscribers at this point and have counted more than 136 billion pageviews so far.

Here’s an interactive demo of Plausible Analytics:

Plausible is bootstrapped and open-source—what made you choose this path instead of taking the more common VC route?

We chose to bootstrap and open source Plausible because we wanted to maintain control and independence while also being more privacy-friendly and transparent.

Both of us have worked at venture funded startups in the past and neither of us had good experiences with investors so going bootstrapped was pretty much the way to do this if we wanted to do things our way.

We’re in the privacy niche so open sourcing our product allows us to build trust as people can inspect our code to verify that our actions match our words. People cannot do that with Google Analytics and other competing products.

Just like Plausible, Wasp is an open-source project too! We’d appreciate it if you could star Wasp on GitHub as a sign of support! ⭐️

Do you have any advice for people who are considering bootstrapping their company? Do you have any books or podcasts to recommend?

I think it’s a good idea to start bootstrapped even if you do wish to get funded. You should focus on creating a great product that solves a real problem and on spreading the word about it. If you do that well, you’ll have investors reaching out to you even if you don’t want or need them.

I recommend reading “Rework” by Jason Fried and David Heinemeier Hansson. It offers unconventional but valuable insights into running a startup.

Another good book is “This Is Marketing” by Seth Godin. It’s about how many startups confuse marketing with spending money on advertising, spamming, interrupting, being annoying and other hacks and tricks. That’s not marketing. Marketing is communication.

How did you get your first customers?

Our first customers came through community engagement and the “build in public” movement. We shared our journey, steps taken and product development openly on our blog, social media and niche communities such as Indie Hackers. That’s how we got the early beta users and some of those became our first subscribers too.

What were the biggest challenges you faced while building and growing Plausible?

The first year was pretty challenging in terms of growth. Uku was working alone on Plausible trying to do both development and marketing. This is pretty much an impossible task. The growth was very slow and we made it to about 100 subscribers and $400 MRR some 14 months into the existence of Plausible.

That’s when Uku decided to look for a marketing co-founder and that’s how we found me. Being two co-founders helped us put more time and effort into marketing and communication. One of the first things we did when I joined was to change our positioning to make it crystal clear and easy to understand what we do, what we stand for and how we compare to Google Analytics (the biggest name in our market). And then we started publishing educational and informative content covering topics such as privacy, open source, bootstrapping and startup marketing .

I have written more about the changes we made in these early days in this post.

Which growth strategies have been the most effective?

We have a boring marketing strategy and we say no to all the growth hacks and other best marketing practices. Content marketing has been our most effective growth strategy. As an example, the first blog post that I published (Why You Should Remove Google Analytics from Your Site) went viral on Hacker News. It drove some good traffic to our site leading to an increase in brand awareness.

What matters is doing quality work and staying consistent with it over a longer period of time so we continued to publish multiple blog posts per week for over a year. Thanks to that work, we’ve been fortunate enough to achieve the viral moments on Hacker News multiple times over those first 2-3 years.

I have shared more about our early years, marketing steps we’ve taken, lessons we’ve learned and things we have achieved in blog posts such as this one. Our analytics dashboard is open to the public so it’s possible to see the progress we’ve made since day one in our stats.

What role has the community played in Plausible’s growth? Have there been any surprising or particularly impactful contributions from the community?

The community has helped shape our product and spread the word about our mission.

We have an open roadmap and listen to the product feedback which determines our development prioritization. This is where feature requests and other feedback is very valuable to us. We pretty much pick the most upvoted feature and work on that.

As mentioned earlier, we don’t do any traditional marketing as in we don’t do any paid advertising nor pay anyone to recommend Plausible. This means that most of our growth comes from people who love using Plausible and who share their experiences with the world. Without people spreading the word about Plausible it would be difficult for us to do what we do. So that’s why community contributions is vital for us.

Plausible Community

What’s next for Plausible? Are there any upcoming features or improvements you’re particularly excited about?

We’re focused on continuing to improve Plausible and making it even more useful and competitive while staying true to our mission and meeting rigorous standards for stability, security and privacy.

Our developers are currently working on the top two most upvoted feature requests from our public feedback board (scroll depth and saved segments) so that’s very exciting. It would be great to release these two big features soon!


Just like Plausible, Wasp is an open-source project too! We’d appreciate it if you could star Wasp on GitHub as a sign of support! ⭐️

Incident Report: Security Vulnerability in Open SaaS "updateCurrentUser" function

On Feb 12th, 2025, we learned about a security vulnerability in the updateCurrentUser function of our Open SaaS template. Users of apps built with Open SaaS can exploit this vulnerability to edit any field in their own User database record, including fields they weren’t supposed to have write permissions for, like subscriptionPlan and isAdmin.

If you created your app with the Open SaaS template before Feb 14th of ‘25, your app potentially suffers from this vulnerability, and you should apply the fix from this report as soon as possible. Check out vulnerability and fix sections.

The vulnerability does not affect the “vanilla” Wasp apps (Wasp apps not built with Open SaaS template) or those that modified the problematic part of the code enough to eliminate the problem.

Since then we fixed the vulnerability in all the versions of the Open SaaS template, did a coordinated vulnerability disclosure (culminating with this report) with the suggested fix, reviewed all the other templates and example apps of ours for similar security vulnerabilities, analyzed what at the first place enabled such omission to happen on our side, and prepared a plan on how to minimize the chance of similar mistakes happening in the future.

We sincerely apologize for the impact and inconvenience caused by our mistake. Caring about code quality is at the center of our culture here at Wasp, but in this instance, we failed to follow up on our standards. We are deeply disappointed by it and will ensure we learn from it, improve, and regain your trust in the code we ship, especially as Wasp is heading from Beta toward 1.0.

The vulnerability

The vulnerability is caused by the updateCurrentUser function in src/user/operations.ts (or in src/server/actions.ts if you used an older version of Open SaaS):

export const updateCurrentUser: UpdateCurrentUser<Partial<User>, User> = async (user, context) => {
if (!context.user) {
throw new HttpError(401);
}
return context.entities.User.update({
where: {
id: context.user.id,
},
data: user, // <- This is the problem!
});
};

While this Wasp Action correctly allows the user to modify only data in their own User database record, and not of other users, it does also allow them to potentially change ANY of the fields on their own User db model, including fields like credits, subscriptionPlan, and isAdmin, due to data: user line in User.update() call. Particularly troublesome is the ability to set isAdmin to true, as it gives them further privileges they shouldn’t have.

An example of how a bad actor could exploit this is by creating a user account in your app, obtaining their own auth credentials via browser dev tools, and then sending a modified request to the HTTP route of updateCurrentUser Wasp Action with a payload that sets the isAdmin field to true for themselves.

The fix

The fix consists of three main steps:

  1. Refactor updateCurrentUser function to updateCurrentUserLastActiveTimestamp
  2. Implement additional Wasp Action(s) for updating user data if needed
  3. Refactor updateUserById function to updateUserIsAdminById

Refactor updateCurrentUser to updateCurrentUserLastActiveTimestamp

In the Open SaaS template, as it comes when you create a new project with it, the Wasp Action updateCurrentUser isn’t used for anything else but updating the lastActiveTimestamp field on the User model, despite its general nature. Therefore, we recommend the following fix:

  1. Rename the operation updateCurrentUser to updateCurrentUserLastActiveTimestamp. Make sure to update its name in all the places: main.wasp, client code (i.e. src/client/App.tsx), server code (i.e. src/user/operations.ts).

  2. Rewrite the operation updateCurrentUserLastActiveTimestamp in src/user/operations.ts so it receives no arguments and only updates the lastActiveTimestamp field on the User:

    export const updateCurrentUserLastActiveTimestamp: UpdateCurrentUserLastActiveTimestamp<void, User> = async (_args, context) => {
    if (!context.user) {
    throw new HttpError(401);
    }
    return context.entities.User.update({
    where: {
    id: context.user.id,
    },
    data: {
    lastActiveTimestamp: new Date(),
    },
    });
    };

    Notice that also the name of the type of the operation changed, so you will want to update the type import, and we also changed the operation’s Input type to void.

  3. Remove all arguments from the call to updateCurrentUserLastActiveTimestamp in src/client/App.tsx:

    if (today.getTime() - lastSeenAt.getTime() > 5 * 60 * 1000) {
    updateCurrentUserLastActiveTimestamp(); // <- no args anymore
    }

Implement additional Wasp Action(s) for updating user data if needed

If you were using updateCurrentUser in your code beyond just updating lastActiveTimestamp, to allow the user to update some other User fields, we recommend also defining additional, more specialized Wasp Action(s) that will handle this additional usage.

For example, let’s say that in your app you additionally defined fullName and address fields on the User model, and you were using updateCurrentUser to allow the user to update those. In that case, we recommend defining an additional Wasp Action called updateCurrentUserPersonalData. It could look something like this:

export const updateCurrentUserPersonalData: UpdateCurrentUserPersonalData<Pick<User, "fullName" | "address">, User> = async (personalData, context) => {
if (!context.user) {
throw new HttpError(401);
}
// NOTE: This is also a good place to do data validation if you want to.
const fullName = personalData.fullName
const address = personalData.address
return context.entities.User.update({
where: {
id: context.user.id,
},
data: { fullName, address }
});
};

Refactor updateUserById to updateUserIsAdminById

Finally, while not a security vulnerability, we also recommend updating the related Wasp Action, updateUserById (you can find it next to where the updateCurrentUser function was), in a similar fashion, to ensure it can’t do more than we need it to:

  1. Rename from updateUserById to updateUserIsAdminById.
  2. Rewrite updateUserIsAdminById to only allow setting the isAdmin field:
export const updateUserIsAdminById: UpdateUserIsAdminById<{ id: User['id'], isAdmin: User['isAdmin'] }, User> = async ({ id, isAdmin }, context) => {
if (!context.user) {
throw new HttpError(401);
}
if (!context.user.isAdmin) {
throw new HttpError(403);
}
if (id === undefined || isAdmin === undefined) {
throw new HttpError(400);
}
return context.entities.User.update({
where: { id },
data: { isAdmin },
});
};

Notice that we modified the shape of the operation input (now it is { id, isAdmin }), so you will also want to update the calls to this operation accordingly.


Additional reading

This second part of the report is not required reading: all you need to know in order to fix the vulnerability is in the “The vulnerability” and the “The fix” sections. But, if you want to learn more about what caused the vulnerability, how we handled it, and what are we doing to prevent similar mistakes from happening in the future, read on!

Coordinated vulnerability disclosure

The challenging part about handling a security vulnerability like this one is that we have to make the knowledge of it public so that all the people with affected apps learn about it and how to fix it, but then at the same time we are also making that knowledge easily available to any bad actors that might want to try to exploit it.

One of the popular approaches is coordinated vulnerability disclosure and it is also what we chose to follow in this instance. We decided to disclose the vulnerability in stages, with 1-week pauses in between:

  1. Stage 1 (private disclosure): We assembled a list of everybody we knew was building and deploying apps with Wasp / Open SaaS, be it from our community on Discord, from online search, or from interacting with them in the past. We privately reached out to everybody on the list and shared the details of the vulnerability and the fix, while also asking them to keep it confidential till we go public with it.
  2. Stage 2 (community disclosure): About a week later, we shared the details of the vulnerability in our Discord community, while again asking people not to share it publicly till we go public with it.
  3. Stage 3 (public disclosure): Finally, a week after the Stage 2, we shared the vulnerability fully publicly.

How did this happen?

TL;DR: Failure in our code review process.

At Wasp, we care a lot about code quality, the code review process, and software craftsmanship in general. PRs get thoroughly reviewed, we do our best to write Clean Code (with a grain of salt), we think a lot about naming, we produce RFCs for any complex feature, our codebase is strongly typed (Haskell and TypeScript), we keep track of all the issues and ideas publicly on our GitHub to not lose sight of them and to also get community input and be transparent.

Commitment to these practices does get tested regularly: Wasp is moving fast and is changing a lot since it is still pre-1.0, so there is always more tech debt going on than one would like, but we always felt like we managed to stay on the good side of our commitment to these practices: they enable us to be efficient but also to enjoy and be proud of our work.

So what happened then, what enabled this vulnerability in Open SaaS?

Open SaaS started as a one-person experiment, a simple template/boilerplate starter for Wasp, so we didn’t do the usual thorough code reviewing of every PR at the very start but thought we would do it fully later, once it shaped up a bit. Also, it is just a template, not a library/framework, people can read/modify the code as they like.

Open SaaS did shape up, and not only have people started using it, but it really picked up, more than we ever expected, and we were getting a lot of positive and constructive feedback, feature requests, ideas, bug reports, … . We started reviewing all the new code thoroughly, but we still haven’t done the full retroactive review. We have done some of it, for parts of more sensitive modules, and some of it happened naturally through refactoring, but we haven’t done it systematically for the whole codebase. We would discuss during every quarterly planning how we should do it this quarter, but there was always something with a higher priority, especially on the Wasp side, and Open SaaS was doing great, if there was anything serious, we would already know about it, we thought.

And then we learned about a function in our codebase that allows a user to set any data, without runtime validation, as a partial update for their User record in the database. This function was barely even used in the Open SaaS codebase at this point: it was used only to update a single field in the User database model, and even that usage should have been refactored into something better already. This function was an obvious code smell, but we never reviewed it properly.

The fact is, we never should have made Open SaaS publicly available without doing a full code review of it first. Once the code is out there, be it just an example app, a template, or a library, we can’t guess how it or its usage will evolve, or how will our priorities evolve. Once an exception in the (review) process is made, it is much harder to find the time to catch up on it, than if we did it when we should have done it in the first place.

What we are doing to prevent similar mistakes

  • No code/documentation goes public without a thorough review. We have been doing this from the very start for the Wasp framework codebase, but we were more lenient with the templates and example apps. From now on, there will be no exceptions.
  • We checked all our existing templates and example apps for vulnerabilities.
  • We have done a thorough review of the Open SaaS template codebase. We have already merged a lot of code quality improvements based on it, and we are in the process of merging the rest.
  • We will make it harder at the Wasp framework level to make a similar mistake. The mistake of passing unvalidated/unparsed data is too easy to make - we will, latest for Wasp 1.0, enforce runtime data validation in Wasp, for Operations, APIs, and other externally facing methods. We also have good ideas for advanced access control support in Wasp, which should further make it harder to make these kinds of mistakes.

Timeline

What follows is the timeline of the actions we have taken since we learned about the vulnerability, in order to minimize its impact:

  • Feb 12th, 2025 (Wed), 10 pm CET: we learned about the vulnerability (thanks Ivan Vlahov!)
  • Feb 13th (Thu):
    • Made an action plan on how to handle the incident, including how we will execute the coordinated disclosure.
    • Fixed all the versions of the Open SaaS template, to prevent new projects from being affected.
  • Feb 14th (Fri):
    • Wrote the “Incident Notification” document with a detailed explanation of the problem and the suggested fix.
    • Compiled a list of the people we know are deploying Open SaaS / Wasp apps and privately shared the “Incident Notification” document with them, giving them ~ a week of head start before we go more public with the incident.
    • Reviewed all the other Wasp templates and example apps for similar security issues.
    • Started a deep (re)review of all the Open SaaS code (that will continue into the next week).
  • Feb 17th (Mon):
    • Continued deep review of Open SaaS code.
  • Feb 18th (Tue):
    • Continued deep review of Open SaaS code.
    • Finalized first draft of this Incident Report document.
  • Feb 19th (Wed):
    • Continued deep review of Open SaaS code.
  • Feb 20th (Thu):
    • Continued deep review of Open SaaS code.
    • Notified our Discord community about the incident by sharing the “Incident Notification” document with them, giving them a week of head start before we go fully public with the incident.
  • Feb 21st (Fri):
    • Finalized the deep review of the Open SaaS code (while continuing with the code improvements).
  • Feb 26th (Wed):
    • Went public with the incident by publishing and sharing this Incident Report.