The reference keeper is a Rust async bot. Source:Documentation Index
Fetch the complete documentation index at: https://continuum-ec12e897.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
keeper/ in the protocol repo.
This page is for operators who want to run their own. Most integrators don’t need to.
Prerequisites
- Rust 1.75+ and Cargo
- Solana CLI 1.18+
- A funded keeper wallet (≥ 0.5 SOL on devnet, more on mainnet)
- A separate CLP authority keypair (≥ 0.1 SOL)
- A private RPC URL (Helius, Triton, etc.) - public devnet is rate-limited
- For arb operation: keeper wallet must be the canonical
market.keeper_authorityfor any market you want to operate (single-pubkey constraint today)
Build
keeper/target/release/keeper (~50MB binary).
Configure
Run
http://localhost:8485 (devnet) or :8484 (mainnet).
Multi-keeper considerations
The market’skeeper_authority is a single pubkey. Two keepers signing as the same pubkey will collide on keeper_*_single calls - only one tx will land per slot.
If you want redundancy:
- Active/standby: peer heartbeat (already in the keeper) + manual primary/standby logic. The reference keeper has basic peer heartbeat; primary/standby logic is on the roadmap.
- Different markets per operator: rotate
keeper_authorityper market.update_keeper_authorityis admin-callable.
Solvency-checking the keeper
Common issues
”Keeper seeder skipping silently”
The most common issue. Check:- Is
USDC_MINTset correctly for your cluster? - Is the global vault balance >
liquidity_seed_threshold(default $1k)? - Is the keeper authority correct? Check
market.keeper_authoritymatches yourKEEPER_KEYPAIRpubkey.
”Unauthorized keeper” on every arb
The keeper signed with a key that doesn’t matchmarket.keeper_authority. Two common causes:
- Wrong
KEEPER_KEYPAIRconfigured. - Admin rotated
keeper_authorityand you didn’t update.
”Position is zombied”
Meteora’sclose_position2 failing with NonEmptyPosition (6030) after multiple remove_liquidity attempts. The keeper detects this automatically and skips the close step. The position’s ~0.05 SOL rent is sacrificed.
If your CLP-state pointer is also lost (rare), recover via:
clp_set_meteora_position to restore the pointer; the keeper drains it on the next tick.
”RPC rate-limited”
The keeper has fallback RPC built in - set a comma-separated list:”Keeper wallet has stranded synth tokens”
Restart the keeper. Boot-time sweep handles it. If the keeper can’t run, manually callredeem_paired (paired residue) or keeper_redeem_single (asymmetric) signed with the keeper authority.
”Pool prices drifted far from NAV”
Diagnostics:- Per-side depegs but combined spread tight → Paired arb can’t close. On mainnet external flow corrects per-side; on devnet you’re alone.
- Pool depth exhausted →
spl-token balance <reserve_y_account>. If near-zero, seed more capital:LIQUIDITY_SEED_FRACTION_BPS=8000. - Zombie positions trapping liquidity → see above.
Production deployment notes
- Multisig the admin keypair. The keeper authority is a hot key by design (signs every cycle). The admin authority should be a Squads multisig - separate from the keeper.
- Run one keeper per cluster. The reference keeper supports dual-network mode (one process, both mainnet and devnet). Separate dashboard ports.
- Monitor the dashboard. It’s on
:8484/:8485. Pipe to your monitoring (Datadog, Grafana, custom Prometheus exporter). - Set up alerts. Watch for: stale oracle (> 60s), unsuccessful arbs in a row (>3), low keeper SOL balance (< 0.1).
- Keep keeper SOL topped up. The keeper pays for every transaction. Burn rate is roughly 0.05 SOL/day on devnet, more under high arb volume.
Logging
continuum_keeper=info- normal operationcontinuum_keeper::arb=debug- every arb decisioncontinuum_keeper::dlmm=debug- every rebalance decisioncontinuum_keeper::rpc=warn- RPC failures only
Updating
See also
Keeper overview
What the keeper does, why it’s privileged.
Arb paths
The four arb cycles.
DLMM management
Position rebalancing details.

