Subscriptions

How subscriptions work — the purchase flow, lifecycle, statuses, and admin management.

A subscription ties a user to a pricing tier for a fixed period. When a subscription is created, a game server is provisioned automatically with the plan's resource limits. When it expires or is canceled, the server is suspended.

Purchase flow

The purchase wizard on /billing/[slug] walks the user through four steps:

  1. Select duration — choose from the available pricing tiers (7-day, 1-month, etc.) and see the price
  2. Pick server type — select an egg (server software template); only eggs allowed by the plan are shown
  3. Configure — choose a Docker image variant and fill in required startup variables
  4. Confirm — enter a server name; the panel checks wallet balance and node capacity, then completes the purchase

On confirmation the panel atomically:

  • Debits the wallet
  • Creates the subscription record with status active
  • Creates the server with the plan's CPU/RAM/disk limits
  • Stores an invoice record for the purchase (there's no invoice view or download for users yet — it's kept in the database only)
  • Records a charge transaction in the wallet history

If any step fails the entire operation is rolled back and the wallet is not charged. Referral bonuses are not part of the purchase flow — they're awarded on wallet top-up (see Wallet & Payments).

Statuses

The subscription status column supports six values, but only three are ever set by the current code:

StatusMeaningSet by
activeSubscription is current; server is runningPurchase, and renewal when the wallet has enough balance
past_dueA renewal charge failed; servers on the subscription are suspendedThe renewal worker, when the wallet balance is too low
canceledSubscription endedNot currently set anywhere — see note below
trialingReserved for a trial periodNot currently used; there is no purchase path that grants a trial, even though trial settings exist
unpaidReserved for repeated renewal failuresNot currently used
pausedReserved for an admin-initiated pauseNot currently used

Lifecycle

  • Renewal — a background worker polls every 60 seconds for subscriptions whose period has ended. It always attempts to renew (there is no auto-renewal toggle to opt out): if the wallet has enough balance it debits it, extends current_period_end, and generates a renewal invoice; if not, it sets the subscription to past_due and suspends (kills and marks suspended) every server on it.
  • Cancel — a user can mark their own subscription with cancelAtPeriodEnd: true. This flag is only used for display today; the renewal worker does not check it, so a "canceled" subscription is still renewed and charged at period end like any other active one. There is no immediate-cancel action.
  • Extension — a user can manually extend their own subscription by purchasing an additional pricing tier period; this charges the wallet immediately and pushes current_period_end forward. There is no separate admin-initiated extension.
  • Pause/unpause — not implemented; there is no code path that sets a subscription to paused.

Because the renewal worker ignores cancelAtPeriodEnd, subscriptions a user has "canceled" keep renewing (and charging the wallet) until the user cancels again after every period, or an admin intervenes directly. Treat this as a known code gap, not intended behavior.

Capacity checking

Before a purchase completes, the panel performs a soft capacity check:

  • It looks at all nodes that are eligible for the plan (considering node restrictions)
  • It checks current allocated RAM and disk (not CPU) against node capacity plus any configured overallocation headroom
  • If no node has enough headroom, the purchase fails with NO_CAPACITY; if there are no free allocations at all, it fails with NO_ALLOCATIONS

Overallocation headroom is configured per-node in the node settings.

Admin visibility

From a user's detail page at Admin → Users → [user] you can see that user's wallet balance, subscriptions, and wallet transaction history, and adjust their wallet balance. There is currently no admin UI to cancel, pause, or extend a subscription directly, and no cross-user subscription list filterable by status or product.

On this page