DEV Community

Cover image for Many shops, one server: running several crypto checkouts from a single Coldtill install
Coldtill
Coldtill

Posted on

Many shops, one server: running several crypto checkouts from a single Coldtill install

If you run more than one store, the usual crypto-payments story gets expensive fast. Each brand wants its own checkout domain, its own webhook endpoint, its own cold wallet and its own API key. Hosted processors usually solve that with another merchant account and another cut of every sale. Self-hosting solves it with another container… unless the gateway was built to keep shops apart inside one install.

Coldtill is built that way. One Linux box, one Docker stack, as many projects as the business needs. Each project is a shop: its own wallets, API keys, webhooks, branding and checkout domain. The rest of this post is how that model actually works day to day, and the few places where people get tangled.

Why projects exist

A "project" is Coldtill's word for one business surface. Think of it as the unit that a customer recognises:

  • Its own store name, logo, accent colour and light or dark theme on the hosted checkout
  • Its own checkout host, such as pay.brand-a.com or pay.brand-b.com
  • Its own cold wallets and deposit wallets per chain
  • Its own API keys and webhook endpoints
  • Its own payment history, customers and deposits

What is not duplicated is the server: PostgreSQL, Caddy, and the four Coldtill processes (api, watcher, sweeper, payouts) stay shared. That matters for ops. You back up one database, watch one set of containers, and upgrade once.

Coldtill dashboard overview

One install, several projects. The dashboard treats each shop as its own surface while the stack underneath is shared.

The design choice behind this is the same one as the rest of Coldtill: the money never belongs to the server. Splitting shops is about isolation of configuration and attribution, not about spinning up a new place that holds customer funds.

What stays separate (and why)

Wallets

Each project points at its own cold wallet addresses. On Ethereum-style chains, Tron and Solana, deposit contracts for that project can only send to that project's cold wallet. Bitcoin deposit addresses come from the project's own wallet pubkey. Mixing brands into one cold wallet is possible in the sense that nothing stops you pasting the same address twice, but it's usually a bookkeeping mistake. Keep them separate and your accountant will thank you later.

Sweep behaviour is also per network and per project rules: funds move in batches when the value justifies the gas, with fee caps, and Tron can rent energy instead of burning TRX. A quiet brand doesn't get swept on the same schedule as a busy one just because they share a host.

API keys

Create a project API key under API keys for each shop. The key scopes calls to that project. If a plugin on Brand A's WooCommerce store leaks its key, Brand B's payments, webhooks and wallets are not reachable with it. Rotate per shop when someone leaves, without touching the others.

Webhooks

Each project has its own webhook URL and signing secret. Brand A's store gets payment.filled events for Brand A only. Replay protection and signature checks are the same mechanism everywhere; the secret is not shared across projects. That also means you can point Brand A at a staging receiver and Brand B at production without rewriting anything global.

Branding and checkout domain

Customers notice this first. The checkout shows the shop's name and logo, not Coldtill's. Support email, help URL and a short message (up to 280 characters) are per project. The checkout can sit on the shop's own domain, so the address bar matches the store the customer just left. Two brands on one server look like two brands, which is the point.

Coldtill dashboard stats

Stats stay scoped to the project you're looking at: revenue, completion rate and where funds still sit on deposit addresses.

Team roles across shops

Coldtill has three roles: admin, developer and read-only.

  • Admin — full control: wallets, payouts, team, project settings.
  • Developer — API keys, webhooks, integration work; not the person approving large payouts.
  • Read-only — payments, customers, deposits and stats for reporting.

On a multi-shop install this maps cleanly onto real teams. A contractor wiring Magento for Brand B gets developer on that project and nothing on Brand A. Finance gets read-only everywhere. Admins stay few. Large payouts still need an admin, a fresh two-factor code and an emailed one-time code, on top of the address book and hourly/daily limits — that gate doesn't loosen just because you added a second project.

A practical layout

Here's a layout that has held up for people running a couple of stores plus a SaaS billing surface:

Project Checkout domain Integrates with Cold wallet
Brand A shop pay.branda.com WooCommerce plugin Hardware wallet A
Brand B shop pay.brandb.com PrestaShop / Magento Hardware wallet B
SaaS invoices pay.saas.example REST API + TypeScript SDK Company treasury

One install at /opt/coldtill, one Caddy, DNS for each checkout host pointed at the same box. Plugins for WooCommerce, PrestaShop, Magento, WHMCS, EmDash and DashCommerce all speak the same REST API and signed webhooks; the difference is which project API key you paste in.

Install itself is still the Free edition one-liner if you're starting fresh:

curl -fsSL https://coldtill.com/install.sh | sudo bash -s -- --edition free
Enter fullscreen mode Exit fullscreen mode

The script lands the stack under /opt/coldtill (PostgreSQL, Caddy, api, watcher, sweeper, payouts). There's no licence server and no approval queue. Source and releases live on the self-hosted crypto payment gateway repo if you want to read the Compose file before you run anything.

How a payment stays in its lane

  1. The shop calls POST /api/v1/payment with its project API key and gets a checkout link for that project.
  2. The customer pays to their own permanent deposit address for that project/chain family.
  3. The watcher confirms, handles reorgs and fires the project's signed webhook.
  4. Later the sweeper moves funds to that project's cold wallet.

Nothing in that path consults another project's wallets or keys. Attribution is by project and by customerID inside the project: each customer has at most one open payment, and deposits on their address credit that payment.

Things that bite when you add a second shop

Same cold wallet pasted twice. Technically fine, operationally messy. Sweeps from two brands land in one place with no label on chain. Prefer separate cold wallets and let the dashboard show each project's unswept total.

Webhook URL copied from the wrong project. The signature will fail or the events will update the wrong store. When you set up a plugin, copy the webhook URL and secret from the project that matches the API key — both are shown in that project's settings.

One shared "ops" API key. Don't. Issue a key per integration. Revoking a compromised Magento key should not mean regenerating every WooCommerce install you run.

Forgetting checkout DNS. Each project can use its own host. If Brand B still serves checkout under Brand A's domain, customers notice, and so do browsers when cookies and HSTS enter the picture. Add the host in the project, put DNS on it, let Caddy terminate TLS.

Assuming stats are global. Open the wrong project and the dashboard looks empty. Switch project first, then panic.

When one server is enough (and when it isn't)

One install is enough when the shops trust the same ops team, share a threat model and can share upgrade windows. It's the common case for a founder with two brands, or an agency hosting a handful of client checkouts they operate.

Split onto another server when the trust boundary changes: a client who must hold their own .env and database, a region with different retention rules, or a shop whose downtime budget can't ride on someone else's upgrade. Coldtill doesn't charge per server — editions differ by sweep fee (Free 2%, Standard 1%, Pro 0%), not by how many boxes you run — so splitting for isolation is an ops choice, not a pricing one.

What I'd do on day one of a second shop

  1. Create the project, set name/logo/colours and the checkout domain.
  2. Attach cold wallets and confirm the first derived deposit address against your own wallet UI.
  3. Create a project API key and a webhook endpoint aimed at that shop only.
  4. Grant the integrator developer, not admin.
  5. Take a testnet payment end to end before mainnet DNS goes live.

If you want to click around before installing, demo.coldtill.com is a live dashboard with made-up data. For the real thing, the install command and docs are on coldtill.com.

One server can absolutely run several crypto checkouts. The work is making sure each shop keeps its own wallets, keys, webhooks and face — and that the people with admin on any of them stay few.

Top comments (0)