Self-hosting an in-app purchase backend: when, cost, how to run
Self-hosting an in-app purchase backend makes sense when you must keep purchase data in a region you choose, when you want no revenue share at scale, or when your company requires software it can read and run itself. With RevenueDot it is one Docker image next to Postgres 16, served on one port. A small app fits on a $12 server, plus $15 for managed Postgres if you want one. The real cost is your time: you own uptime, backups and upgrades.
The short answer#
- Run it:
git clone, copy.env.exampleto.env, setPOSTGRES_PASSWORD, rundocker compose up -d. - What runs: one
revenuedotcontainer (SDK API, REST API, store notifications and the dashboard) and one Postgres 16 container with a persistent volume. - Cost: the software is free (AGPL-3.0). Servers start at about $12 a month at list prices.
- Choose it for: data residency, no revenue share, running inside your own cloud, or code you can read.
- Skip it if: you want no operations work. RevenueDot Cloud is free up to $10,000 in monthly tracked revenue.
When self-hosting makes sense#
| Reason | Why self-hosting fits | Alternative |
|---|---|---|
| Data residency | Purchases, customers and receipts live in your own Postgres, in the region you pick | Cloud, if its region and terms are enough for you |
| Cost at scale | No revenue share and no limit on tracked revenue. RevenueCat's 1% of tracked revenue is $500 a month at $50,000 | The planned RevenueDot Cloud price, 0.5% above $10,000, capped at $999 |
| Regulated apps | You control who can read data and how long it is kept | Ask your compliance team what a hosted vendor must show |
| Agencies with many apps | One server holds many projects and apps | Cloud projects |
| Reading and changing the code | The server decides who gets paid access. You can read it, test it and patch it | Not possible with a closed service |
| An internal network | The server can sit behind your own firewall, reachable by Apple and Google over HTTPS only | Not possible on a hosted service |
It does not make sense when you are small, do not want to be on call, and the free tier of a hosted service already covers you. See RevenueCat pricing in 2026 for the break-even sums.
Data residency in plain terms#
If your customers are in the EU, you may need to keep personal data in the EU or rely on a legal transfer mechanism. The European Commission's page on international data protection lists adequacy decisions and standard contractual clauses as the two main routes for transfers outside the EU and EEA. Self-hosting is the simplest way to keep the database in a region of your choice, for example Frankfurt or Amsterdam. DigitalOcean's regional availability page lists Frankfurt (FRA1) and Amsterdam (AMS3), among other regions, for both servers and managed databases. Other clouds offer EU regions too. Remember that the stores themselves (Apple, Google) still process purchases under their own terms. This is not legal advice.
What it costs#
These are list prices checked on October 1, 2026. They change, so check before you plan.
| Option | Price | What you get |
|---|---|---|
| DigitalOcean Droplet, Basic | $12 a month | 2 GiB RAM, 1 vCPU, 50 GiB SSD, 2,000 GiB transfer |
| DigitalOcean Droplet, larger | $24 a month | 2 GiB RAM, 2 vCPU, 60 GiB SSD, 3,000 GiB transfer |
| DigitalOcean managed PostgreSQL | $15.15 a month and up | 1 GiB RAM, 1 vCPU, 10 to 30 GiB storage |
| Railway Hobby | $5 a month with $5 of included usage | Memory at $10 per GB a month and CPU at $20 per vCPU a month beyond that |
| Your own hardware or cloud account | Your cost | Everything |
A sensible starting setup is a $12 Droplet that runs the Compose file, which includes Postgres, or the same Droplet plus the $15.15 managed database. That is $12 to about $27 a month. At $50,000 of monthly tracked revenue, RevenueCat bills $500, so the saving is over $470 a month, or about $5,700 a year. We have not load-tested RevenueDot at your scale. A large app should size the server and the database by watching real use, and may need a bigger plan.
Add the costs that are not on a price list:
- Backups. Object storage for daily dumps is cheap, and a managed database has point-in-time recovery.
- Email. Password resets, team invites and alerts need an SMTP provider. Many have free tiers.
- Domain and TLS. A domain, and a proxy such as Caddy that renews certificates itself.
- Monitoring. An uptime check on
GET /v1/health. - Your time. The first start takes a few minutes, mostly the image build. HTTPS, email and backups take longer. After that you watch alerts and run upgrades, until something breaks.
The break-even against RevenueCat's published rule, 1% of all tracked revenue once above $2,500, is a $27 server at about $2,700 of monthly revenue. Below roughly $10,000 a month, RevenueDot Cloud is free, so self-hosting there is about control, not cost.
How to run it#
You need Docker, Docker Compose, a server with a public IP and a domain name. The self-hosting guide has every setting.
Step 1: Start it#
git clone https://github.com/revenuedot/revenuedot.git
cd revenuedot
cp .env.example .env # set POSTGRES_PASSWORD before the first start
docker compose up -d # builds the image and starts RevenueDot and Postgres
curl http://localhost:8787/v1/health # {"status":"ok"}There is no published image yet. Compose builds it from source, so the first start takes a few minutes. The server applies database migrations itself when it starts.
Open http://localhost:8787/login and sign up. The first account is the owner. After that, sign-up closes to everyone except the people you invite, unless you set REVENUEDOT_ALLOW_SIGNUP=true.
Expected output: {"status":"ok"} from the health check, and the dashboard at /login.
Step 2: Put HTTPS in front#
Apple and Google send store notifications only to public HTTPS URLs, and apps should never talk to your server over plain HTTP. Caddy gets and renews certificates by itself:
# Caddyfile
revenuedot.example.com {
reverse_proxy localhost:8787
}RevenueDot builds the notification URLs and the proxy URL it shows in the dashboard from X-Forwarded-Host and X-Forwarded-Proto. Check that the app page shows https://revenuedot.example.com/v1/notifications/....
Step 3: Set the important variables#
| Variable | What it does |
|---|---|
POSTGRES_PASSWORD |
Password of the bundled Postgres. Set it before the first start, because it is written into the volume then |
REVENUEDOT_PORT |
Host port, default 8787 |
REVENUEDOT_ALLOW_SIGNUP |
Default false. Only the owner can sign up, plus invitees |
REVENUEDOT_SMTP_URL, REVENUEDOT_MAIL_FROM, REVENUEDOT_PUBLIC_URL |
Outgoing email for resets, invites and alerts. Without SMTP, emails are printed to the server log |
REVENUEDOT_SIGNING_KEY |
Turns on response signing. Optional |
DATABASE_URL |
Use your own Postgres 16, for example a managed one, and remove the db service |
Run one revenuedot container per database. Its background job has no lock across processes, so two containers could send a webhook twice.
Step 4: Connect your stores and your app#
Add each store app, its credentials and its notification URL, as in the App Store guide and the Google Play guide. Then point the SDK at your server:
Purchases.proxyURL = URL(string: "https://revenuedot.example.com")!
Purchases.configure(
with: Configuration.Builder(withAPIKey: "appl_YourKey")
.with(entitlementVerificationMode: .disabled)
.build()
)Every SDK has the same setting. The SDK guides show each one. A self-hosted server signs responses with its own key, so the stock SDKs should keep verification disabled.
Step 5: Back up#
Everything is in Postgres, including store credentials, so encrypt backups. A daily dump:
docker compose exec -T db pg_dump -U revenuedot -Fc revenuedot > revenuedot-$(date +%F).dumpCopy the file off the server, and do one test restore before you need it. Keep REVENUEDOT_SIGNING_KEY in a password manager, because it is not in the database. See Backups.
Step 6: Upgrade#
docker compose exec -T db pg_dump -U revenuedot -Fc revenuedot > revenuedot-before-upgrade.dump
git pull
docker compose up -d --build
curl -s http://localhost:8787/v1/healthRequests fail for a few seconds during the restart. The SDKs keep the purchase on the device and retry, and customer info comes from the SDK's cache. Apple and Google resend notifications that got no 2xx answer. Pending webhooks wait in the database. See Upgrades.

What happens when your server is down#
This is the question every self-hoster asks. Short outages are survivable:
- Paying customers keep access. When the server answers 5xx or is unreachable, the SDKs compute entitlements on the device from the store's record of purchases, using a product-to-entitlement mapping they cached. See offline entitlements.
- Apple retries. For V2 notifications, five times, at 1, 12, 24, 48 and 72 hours after the previous attempt in production (Apple). Apple's Get Notification History endpoint covers 180 days of missed messages.
- Google retries through Pub/Sub when your endpoint answers 500 or 503.
- Purchases wait. An app keeps an unposted purchase and tries again when your server returns.
Long outages are another matter. New customers cannot finish a purchase in your backend, and webhooks stop. Use uptime monitoring and a runbook. The production checklist lists what to check before you go live.
Honest limits#
- RevenueDot launched in 2026, so it has far less production history than RevenueCat. Run your own sandbox purchase before you depend on it.
- There is no published Docker image, so Compose builds from source.
- Run one server container per database for now.
- You carry security patches, backups and on-call duty.
- RevenueCat has years of production use and a SOC 2 Type II audit. If you need that paper today, check whether you can meet your own review another way.
Do it with RevenueDot#
- Try the same software on RevenueDot Cloud first. It is free up to $10,000 in monthly tracked revenue, and it uses the same code and API as self-hosting, so you can move either way.
- When you are ready, run the Compose file on a $12 server and point a domain at it.
- Add HTTPS, SMTP and a daily backup.
- Connect your stores and set the proxy URL in a test build.
- Import from RevenueCat and run both systems side by side with the migration guide.
Start free on RevenueDot Cloud
FAQ#
How much does it cost to self-host an in-app purchase server?#
The RevenueDot software is free. A small app runs on a $12-a-month server at DigitalOcean list prices, or about $27 with a managed Postgres database. Backups, email and your own time are extra.
What do I need to run RevenueDot myself?#
Docker with Compose, a server with a public IP, a domain with HTTPS in front, and Postgres 16, which the Compose file includes. Apple and Google must be able to reach the notification URLs over HTTPS.
Does self-hosting give me data residency?#
You choose where the server and the database run, so you can keep purchase data in a region such as Frankfurt. Apple and Google still process the purchases themselves. Ask a lawyer about your own obligations.
Can I move between Cloud and self-hosted later?#
Yes. Cloud and self-host run the same code and the same API, so you can start on one and move to the other. Your SDK change is only the proxy URL.
Can I run more than one server for high availability?#
Not yet. Run one RevenueDot container per database. The background job has no lock across processes, so two containers could send a webhook twice.
About RevenueDot. RevenueDot is an open-source (AGPL-3.0) backend for in-app purchases and subscriptions that works with the RevenueCat SDK. Start free on RevenueDot Cloud, free up to $10,000 in monthly tracked revenue, or self-host it with Docker and Postgres. Point the SDK's proxy URL at RevenueDot and keep your app code, your offerings and your customers. Read the quickstart or the code on GitHub.