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, runufw 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:
rsync -av --exclude node_modules --exclude .next \
--exclude 'contracts/lib' --exclude 'contracts/out' \
./ root@<your-vps-ip>:/opt/kepto/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 belowThe 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.
<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.
docker compose up -d --build
docker compose logs -f4Fund 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:
docker compose logs -f providerOnce 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:
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.
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/ProofSubmittedactivity 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:
# 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_KEY9Unstake and exit
deactivate()on the registry — stops new deals; existing deals continue and must still be served and proven.- Let all active deals finish and be settled.
- Wait out the 7-day unstake delay from deactivation.
withdrawStake()— returns your full stake. A deactivated provider can come back any time withreactivate().
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