Autonnel v0.1.0
Back to blog

Self-hosting a checkout: what you are actually signing up to run

The operational bill for self-hosting a checkout: Postgres backups, two secrets you must never rotate carelessly, and where card data does not go.

· 6 min read

Our own comparison pages say it: “If nobody on your team wants to touch infrastructure, pay them.” This post is the long version of that sentence, written so you can decide before you install rather than after.

Nothing here is an argument against self-hosting. It is the list of things that become yours when you do.

Postgres, and specifically its backups

Autonnel needs one Postgres database. Standing it up is easy; docker compose up brings one with it and applies the schema. That is not the part that matters.

The part that matters is that your orders live in it. Not a copy of your orders, the orders. If you lose that database you have lost the record of who paid you, what for, and whether it shipped.

So the real requirement is not “run Postgres”, it is:

  • automated backups on a schedule you chose deliberately
  • a restore you have actually performed at least once, on purpose, before you needed to
  • somewhere to restore to

The second bullet is the one people skip. A backup you have never restored is a hypothesis. Managed Postgres from a provider that does point-in-time recovery removes most of this work and costs roughly $20 to $25 a month for a small instance, which is the single largest line item in a self-hosted deploy. I would take that trade every time over running Postgres yourself, and the tool does not care which you choose.

Two secrets, and the specific way they ruin your day

Two environment variables are required whenever NODE_ENV is not development or test, which includes the published Docker image:

AUTH_SESSION_SECRET=$(openssl rand -hex 32)
CREDENTIALS_ENCRYPTION_KEY=$(openssl rand -base64 32)

The compose file ships insecure development defaults so the first run needs zero configuration. Replace both before anything faces the internet. That is the obvious half.

The non-obvious half is what happens if you regenerate them later, which is exactly what a well-meaning “rotate all secrets” runbook would do:

  • rotating AUTH_SESSION_SECRET invalidates every active session, which is annoying but survivable
  • rotating CREDENTIALS_ENCRYPTION_KEY makes stored provider credentials unreadable

That second one means your Stripe keys, PayPal credentials, email provider config and S3 credentials are all encrypted with a key you just threw away. Nothing is corrupt, everything is fine, and none of it can be decrypted. You reconfigure every provider by hand.

Generate each value once, store it wherever you store things you cannot regenerate, and treat it as permanent. If you genuinely must rotate CREDENTIALS_ENCRYPTION_KEY, plan it as a migration: decrypt with the old key, re-encrypt with the new one, in that order, with a backup taken first.

Upgrades

Images are published to GHCR, multi-arch, with tags that let you pick your own risk level:

TagBehaviour
:latestmost recent stable
:1.3.0exact version, recommended for production
:1.3auto-updates on patch releases
:1auto-updates on minor and patch releases

Pin the exact version in production. The :1 tag is convenient right up until a minor release changes something on a Tuesday afternoon you had other plans for.

Schema changes ship with the image, so upgrading means pulling the new tag and applying migrations. The container includes a HEALTHCHECK against /api/health, which verifies database and cache connectivity, so your orchestrator can tell the difference between “started” and “actually working”. Point your monitoring at the same endpoint.

Where card data goes, and where it does not

This is the question people are too embarrassed to ask, so: card numbers do not touch your server or your database.

Stripe and PayPal handle the card entry and the tokenisation. Stripe brings automatic SCA and 3DS support with no code on your side. What Autonnel stores is an order, a reference to the charge, and the transaction records. The upsell charges the saved card through the provider, which is why there is no second checkout, and why the card details are still not yours to hold.

What that means for your PCI obligations depends on your integration and your volume, and I am not qualified to hand you an SAQ level. The mechanism is what I can tell you: the sensitive data path runs from the customer’s browser to the payment provider, not through your infrastructure. Confirm the paperwork with your acquirer, but you are not building a card vault.

You are still responsible for the boring security around it: TLS, keeping the admin domain off the public funnel domain, the two secrets above, and not leaving the setup wizard reachable after you have used it.

What you get in exchange

The trade is not one-sided or nobody would take it.

No fee attached to your success. No per-order charge, no upsell revenue share, no per-contact pricing tier. The infrastructure costs what the infrastructure costs, and it does not care how well the funnel converts.

The money path is code you can read. When checkout does something surprising, you can open the file. That is a different debugging experience from filing a support ticket about someone else’s black box.

Nothing stops when you stop paying. There is no subscription that, when it lapses, takes your funnels offline. Apache-2.0, no commercial-use restriction, no agency tier.

Your data is in your database. Export is a pg_dump, not a feature request.

Who should not do this

I would rather you self-select out now than resent it later.

If nobody on the team owns infrastructure, pay for a hosted platform. A funnel that is live on someone else’s servers beats one that is half-configured on yours. This is the honest recommendation and it applies to a lot of people.

If you cannot answer “who gets paged when checkout breaks at 2am”, either answer it or do not self-host. The failure mode is not that the site goes down, it is that the site takes orders and something downstream quietly does not work.

If you want the money path yours but not the servers, Cloud is the same application with the operations handled. The escape hatch stays open: it is the same code under the same licence, so moving to self-hosted later is a migration, not a rebuild.

Try it before you commit to any of this

curl -O https://raw.githubusercontent.com/autonnel/autonnel/master/docker-compose.yml
docker compose up

It boots without a database URL, an S3 bucket, an email provider or a connected store. Everything above is what production needs, not what a look around needs.

Full details in installation, the product-level overview at self-hosted checkout page builder, and the cost model for the Workers deploy path in what a funnel costs on Cloudflare Workers.