Provider Docs

Run a storage provider

A Kepto provider stores file bytes on disk, answers on-chain storage challenges, and earns KEP as deal escrow vests. This guide takes you from a bare VPS to a registered, earning provider with the one-box docker deploy. For what you earn and what is at stake, see Provider economics; for the daemon's HTTP surface, see the Daemon HTTP API.

1Hardware and prerequisites

  • An always-on VPS — 2 vCPU, 2–4 GB RAM, 40 GB SSD is plenty to start (Hetzner CX22 ~€4/mo, or a $6/mo DigitalOcean/Vultr droplet). Use Ubuntu 24.04 and note the public IP.
  • A public HTTPS endpoint — the registry stores your base URL and clients connect to it directly. Point a subdomain's A record at the VPS before launch; the bundled Caddy issues the TLS certificate automatically once DNS resolves.
  • Stable disk — you advertise a capacity on-chain and must keep every active deal's chunks. Losing data means failing challenges and being slashed.
  • Open ports 80 and 443 — if you enabled ufw, run ufw allow 80 && ufw allow 443.

2Copy the code and configure

From the repo root on your machine, rsync the code onto the box, then install Docker and fill in deploy/.env:

Your machine
rsync -av --exclude node_modules --exclude .next \
  --exclude 'contracts/lib' --exclude 'contracts/out' \
  ./ root@<your-vps-ip>:/opt/kepto/
On the VPS
ssh root@<your-vps-ip>
curl -fsSL https://get.docker.com | sh
cd /opt/kepto/deploy
cp .env.example .env
nano .env   # fill in the values below

The keys to set in .env:

  • GATEWAY_DOMAIN — the subdomain you pointed at the box; Caddy serves it with automatic HTTPS.
  • PROVIDER_PRIVATE_KEY — the provider EOA's key. It signs register, accept, activate, proof, and withdraw transactions. Use a fresh key on testnet.
  • GATEWAY_PRIVATE_KEY / OPERATOR_PRIVATE_KEY — only needed if you also run the public gateway and the equity operator bot; comment those services out of the compose file if you only want storage.
!Heads up
Never commit or paste real private keys. Everything in this guide uses <your-vps-ip> and env-var placeholders — substitute your own values locally.

3Launch

One command builds and starts everything (provider daemon, Caddy, and optionally the gateway and operator bot). The first build takes a few minutes.

On the VPS
docker compose up -d --build
docker compose logs -f

4Fund the wallets

The provider wallet needs a little native gas and enough KEP for stake plus collateral headroom:

  • Native gas: paste the address into the chain faucet at faucet.testnet.chain.robinhood.com. Every provider transaction pays gas in ETH.
  • KEP: the token faucet mints 1,000 KEP per address per 24 h — claim from the web app at /app/faucet or via the CLI (npm run -w @kepto/sdk cli -- faucet). You need at least 1,000 KEP for the registry stake, plus 50% of each incoming deal's price as collateral while it runs. Repeat daily until you have headroom.

5Auto-register on boot

With AUTO_REGISTER on (the default), the daemon approves the 1,000 KEP stake and registers itself in the on-chain ProviderRegistry — endpoint, capacity, and advertised price — as soon as the wallet is funded. Watch it happen:

On the VPS
docker compose logs -f provider

Once registered you are listed on /app/providers and clients can create deals against you. With AUTO_ACCEPT on, the daemon accepts deals that pass its policy (minimum price per GiB-epoch, maximum size, capacity headroom), locks collateral, verifies the uploaded bytes against the on-chain Merkle root, and activates the deal — all automatically.

6Verify

Confirm the daemon is reachable from the public internet — this is the same URL the registry hands to clients:

Anywhere
curl https://<your-provider-domain>/v1/info
# → { "provider": "0x…", "chainId": 46630, "market": "0x…",
#     "capacityBytes": …, "usedBytes": …, "autoAccept": true, "version": "0.1.0" }

7Monitor

Uptime is money. Any KEP holder can challenge your active deals (at most once per 12 h per deal), and the daemon must answer with a Merkle proof within 8 h or you are slashed a third of the deal's collateral per fault. The daemon answers automatically and is paid the 10 KEP challenge bond for each successful proof — an online honest node profits from being challenged.

Operations
docker compose logs -f provider   # tail the provider daemon
docker compose restart provider   # restart after an .env change
docker compose down               # stop everything (volumes persist)
  • Watch the logs for ChallengeIssued / ProofSubmitted activity and RPC errors.
  • Keep the wallet topped up with gas — a daemon that cannot pay gas cannot prove.
  • Never delete anything under the data volume while deals are active — losing chunks means failing challenges.
  • Inspect any deal from either side with GET /v1/deals/:dealId (local + on-chain view).

8Withdraw earnings

Earnings vest per second over each deal's duration. The daemon claims them for you on a timer — and only when the claim is worth more than about twice the transaction cost, so gas never eats the earnings (see gas-aware withdrawals). You can also claim manually:

Manual (cast)
# pull vested earnings on an active deal (provider only)
cast send $KEPTO_MARKET "withdrawEarnings(uint256)" <dealId> \
  --rpc-url $RPC --private-key $PROVIDER_PRIVATE_KEY

# after endTime (anyone may call): pays remaining escrow + returns collateral
cast send $KEPTO_MARKET "settleDeal(uint256)" <dealId> \
  --rpc-url $RPC --private-key $PROVIDER_PRIVATE_KEY

9Unstake and exit

  1. deactivate() on the registry — stops new deals; existing deals continue and must still be served and proven.
  2. Let all active deals finish and be settled.
  3. Wait out the 7-day unstake delay from deactivation.
  4. withdrawStake() — returns your full stake. A deactivated provider can come back any time with reactivate().
Manual (cast)
cast send $KEPTO_REGISTRY "deactivate()" \
  --rpc-url $RPC --private-key $PROVIDER_PRIVATE_KEY
# … after all deals settled and 7 days elapsed …
cast send $KEPTO_REGISTRY "withdrawStake()" \
  --rpc-url $RPC --private-key $PROVIDER_PRIVATE_KEY