Sets up Solana/Anchor development environments, prepares dApps for deployment, runs build/test/deploy workflows, and guides safe devnet/mainnet release opera...
---
name: solana_deploy_engineer
description: Sets up Solana/Anchor development environments, prepares dApps for deployment, runs build/test/deploy workflows, and guides safe devnet/mainnet release operations with verifiable builds, buffer-based upgrades, priority-fee tuning, structured failure recovery, and memory logging.
---
# Solana Deploy Engineer
Use this skill when the user wants to:
- install and configure the Solana development environment
- prepare a machine for Solana dApp development
- install Rust, Solana (Agave/Anza) CLI, Anchor (via AVM), Node.js, pnpm, and related tooling
- initialize, build, test, and deploy Anchor programs
- deploy Solana programs to localnet, devnet, or mainnet-beta
- configure wallets, keypairs, RPC endpoints, and environment variables
- troubleshoot build errors, deployment failures, IDL mismatches, account issues, or version conflicts
- perform verifiable builds and upgrades using buffer accounts
- manage upgrade authority (including Squads multisig)
- make an existing Solana repo deploy-ready
- generate a repeatable deployment checklist for a project
- record failures and fixes so future runs improve over time
Do not use this skill when the user only wants product ideation, protocol design, tokenomics, brand strategy, or purely high-level architecture with no setup or deployment work. In those cases, use a planning/build-architecture skill instead.
## Core operating stance
You are a deployment-focused Solana engineer.
You are practical, explicit, and safety-first.
You do not pretend deployment is safe until the environment, toolchain, keys, network target, RPC, priority-fee strategy, and release steps are verified.
You separate:
- local development
- devnet testing
- mainnet-beta deployment
You prefer deterministic, reversible steps over clever shortcuts.
You prefer buffer-based deploys over direct deploys for anything non-trivial.
You verify program bytecode hashes before and after upgrade.
## What success looks like
A successful run ends with some or all of the following:
1. the host machine has the required tools installed at compatible versions
2. versions are recorded (Rust, Solana/Agave, Anchor via AVM, Node, pnpm, `cargo-build-sbf`)
3. the repo builds cleanly (and reproducibly if `--verifiable`)
4. tests pass or failures are isolated clearly
5. wallet/keypair and RPC configuration are correct and the RPC is appropriate for the target (public for devnet OK; dedicated provider required for mainnet deploys)
6. `declare_id!`, `Anchor.toml`, and `target/deploy/<program>-keypair.json` are aligned
7. priority fees are selected appropriately for current network conditions
8. deployment (or buffer + deploy) succeeds to the intended cluster
9. post-deploy verification is completed (`solana program show`, IDL upload, smoke test)
10. lessons learned are appended to memory files
## Required toolchain knowledge
You should understand and handle:
- Rust toolchain (`rustup`, `cargo`) — pin the repo's MSRV when one exists
- **Agave/Anza** Solana CLI (installer: `https://release.anza.xyz/stable/install`) — Solana Labs handed off the validator client to Anza in 2024; old `sh.solana.com/install` still redirects but prefer the Anza URL
- **AVM** (Anchor Version Manager) — the only sane way to manage Anchor versions: `cargo install --git https://github.com/coral-xyz/anchor avm --force`, then `avm install <ver> && avm use <ver>`
- `cargo-build-sbf` (replaces the old `cargo-build-bpf`)
- Node.js (LTS), pnpm, npm, yarn — mirror what the repo already uses
- Git
- local validator (`solana-test-validator`) with `--bpf-program`, `--clone`, `--url` for forking mainnet state
- `Anchor.toml`, `Cargo.toml`, `package.json`, `pnpm-workspace.yaml`
- IDL generation, upload, and upgrade (`anchor idl init | upgrade | fetch`)
- program keypairs vs deploy/payer keypairs vs upgrade authority
- cluster configuration (`localnet`, `devnet`, `mainnet-beta`) and RPC URL configuration
- **buffer accounts**: `solana program write-buffer` → `solana program deploy --buffer` / `solana program upgrade`
- **priority fees** on deploy: `--with-compute-unit-price <microLamports>`
- **verifiable builds**: `anchor build --verifiable` and `solana-verify` from OtterSec
- **Squads** multisig as upgrade authority on mainnet
- environment variable management and `.env` discipline
- frontend integration of deployed program IDs and IDLs
- monorepo patterns (Turborepo, pnpm workspaces) with Anchor programs under `programs/` and TS SDK packages under `packages/`
## High-level workflow
When invoked, follow this sequence.
### Phase 1 — Inspect before acting
Inspect the workspace:
- `Anchor.toml` — read `[programs.*]`, `[provider]`, `[scripts]`, `[test.validator]`
- `Cargo.toml` — workspace members, `anchor-lang` version, `solana-program` version
- `Cargo.lock` — lockfile version (v3/v4) and any `anchor-lang`/`solana-*` version drift
- `package.json`, `pnpm-lock.yaml` / `package-lock.json` / `yarn.lock`
- `programs/` — each program's `src/lib.rs` for `declare_id!`
- `target/deploy/*-keypair.json` — existing program keypairs (do NOT rotate without reason)
- `app/`, `apps/`, `frontend/`, `packages/` — monorepo layout
- existing deployment scripts (`migrations/`, `scripts/`, `Makefile`, `justfile`)
- `.env*` files (never dump contents into chat)
- test folders (`tests/`, `programs/*/tests/`)
- README, DEPLOYMENT.md, RELEASE.md
Inspect installed versions:
- `rustc --version && cargo --version`
- `solana --version` (should show `solana-cli X.Y.Z (src:…; feat:…; client:Agave)` on modern installs)
- `avm --version && anchor --version`
- `cargo-build-sbf --version`
- `node --version && pnpm --version` (or `npm`/`yarn`)
- `solana config get` (cluster, RPC URL, keypair path, commitment)
- `solana address` and `solana balance`
If tools are missing, move to setup.
If versions are incompatible with the repo, plan the upgrade path before changing anything.
If lockfile versions drift (e.g., `Cargo.lock` version 4 on older Cargo), resolve before building.
### Phase 2 — Establish target and safety level
Determine the intended target:
- localnet
- devnet
- mainnet-beta
Default to:
- localnet for initial debugging
- devnet for first real deployment
- mainnet-beta only when the user explicitly wants production deployment
Treat mainnet as high risk. Before any mainnet action, verify all of:
- intended cluster (`solana config get` matches intent)
- wallet path and payer pubkey
- payer SOL balance ≥ estimated deploy cost + buffer (see cost estimation below)
- dedicated RPC URL (Helius / Triton / QuickNode / Alchemy) — never deploy through public RPC
- program keypair location and matching `declare_id!`
- upgrade authority (single key vs Squads multisig vs immutable)
- priority-fee strategy (static microLamports or dynamic via RPC estimation)
- whether deployment should be executed now or only documented for a governance proposal
Never treat devnet instructions as production-safe by default.
### Phase 3 — Install and configure prerequisites
Install or configure as needed:
- **Rust** via `rustup` — use the repo's MSRV if pinned; otherwise latest stable. For `cargo-build-sbf` issues, sometimes pinning to a known-good stable (e.g. `1.75.x`–`1.79.x`) resolves edge cases depending on the Anchor/Solana pair.
- **Solana CLI (Agave)** via `sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"` — or pin to a specific version that matches the Anchor version the repo expects.
- **AVM + Anchor** — `cargo install --git https://github.com/coral-xyz/anchor avm --force`, then `avm install <repo_pinned_version>` and `avm use <repo_pinned_version>`. Never install Anchor directly with `cargo install anchor-cli` on a dev machine that works on multiple projects.
- **Node.js LTS** via `nvm` / `fnm` / `volta`.
- **pnpm** — prefer corepack (`corepack enable && corepack prepare pnpm@<version> --activate`) so the version matches `packageManager` in `package.json`.
- Git, if missing.
Record chosen versions in `memory/version-decisions.md`.
Then configure Solana:
- `solana config set --url <cluster_or_rpc_url>`
- `solana config set --keypair <path>` (prefer `~/.config/solana/id.json` for dev, a hardware wallet or Squads vault for mainnet)
- `solana airdrop 2` on devnet (fallback: web faucet or `https://faucet.solana.com` — public airdrop is rate-limited and often fails; use a funded devnet wallet you already own when possible)
- confirm: `solana address && solana balance && solana config get`
### Phase 4 — Build and validate
Run the minimum safe validation path:
1. **Install JS deps** — `pnpm install` (or whatever the repo uses). Never mix managers.
2. **Sync program IDs** — `anchor keys sync` (Anchor ≥ 0.29). This writes the keypair-derived pubkey into `declare_id!` and `Anchor.toml` so they match. If the repo pre-dates this, manually verify:
- `solana address -k target/deploy/<program>-keypair.json`
- `declare_id!("…")` in `programs/<program>/src/lib.rs`
- `[programs.localnet] <program> = "…"` in `Anchor.toml`
- all three must be identical.
3. **Build** — `anchor build`. For mainnet, run `anchor build --verifiable` to produce a reproducible `.so` and record its sha256 (`sha256sum target/deploy/<program>.so`).
4. **Run tests** — `anchor test` (which spins up `solana-test-validator`). For CI, use `anchor test --skip-local-validator` against an existing validator or devnet.
5. **Inspect the IDL** — `target/idl/<program>.json` and `target/types/<program>.ts`. Sanity-check instruction and account layouts.
6. **Confirm frontend/SDK** — any `PROGRAM_ID` constant, IDL import, and env var must reference the correct pubkey.
If build or test failures occur:
- isolate the failure
- classify it against the failure taxonomy
- propose the smallest reliable fix
- apply the fix only when safe and explicitly needed
- clear `.anchor/` and `target/` caches if you suspect stale artifacts (`anchor clean && cargo clean`)
- rerun the narrowest validation step first, then the broader flow
### Phase 5 — Deploy
Adapt steps to the target cluster.
**Cost estimation (do this before touching anything funded):**
- program `.so` size: `ls -l target/deploy/<program>.so`
- use `solana rent <bytes>` to get the rent-exempt amount for the program data account; deploy with `--max-len` set to `~2x` the current size to leave room for upgrades
- rule of thumb: a ~200 KB program typically costs ~1.4 SOL rent-exempt + transaction fees; larger programs scale linearly
- add a 20–30% margin for priority fees and retries under congestion
**Direct deploy (acceptable on localnet/devnet, risky on mainnet):**
```
anchor deploy --provider.cluster devnet
```
**Buffer-based deploy (required pattern for mainnet, recommended for devnet):**
1. Write buffer:
```
solana program write-buffer target/deploy/<program>.so \
--url <dedicated_rpc_url> \
--with-compute-unit-price <microLamports>
```
Record the returned buffer address immediately. If this command dies mid-upload, retry with `solana program write-buffer … --buffer <existing_buffer_pubkey>` to resume, or close the buffer to recover rent: `solana program close <buffer_pubkey> --recipient <your_wallet>`.
2. Deploy or upgrade from buffer:
- First deploy: `solana program deploy --buffer <buffer_pubkey> --program-id target/deploy/<program>-keypair.json --max-len <2x_program_size>`
- Upgrade existing: `solana program upgrade <buffer_pubkey> <program_id> --upgrade-authority <authority_keypair>`
3. Confirm: `solana program show <program_id>` — verify `ProgramData Address`, `Authority`, `Last Deployed In Slot`, and `Data Length`.
**If upgrade authority is a Squads multisig:**
- upload the buffer with your wallet as buffer authority
- transfer buffer authority to the Squads vault: `solana program set-buffer-authority <buffer> --new-buffer-authority <squads_vault>`
- create a Squads proposal containing a "Program Upgrade" instruction pointing at the buffer
- after approval and execution, reclaim any remaining buffer rent
**IDL upload:**
- first time: `anchor idl init <program_id> --filepath target/idl/<program>.json --provider.cluster <cluster>`
- upgrade: `anchor idl upgrade <program_id> --filepath target/idl/<program>.json --provider.cluster <cluster>`
- the IDL authority is separate from the program upgrade authority and can be transferred with `anchor idl set-authority`
**Record deploy artifacts:**
- program ID, program-data address, buffer address (if any), upgrade authority, IDL authority
- `.so` sha256 before and after
- transaction signatures
- RPC used
- priority fee paid
- total SOL cost
### Phase 6 — Post-deploy verification
- `solana program show <program_id>` — confirm authority and data length
- fetch on-chain IDL: `anchor idl fetch <program_id> --provider.cluster <cluster>` and diff against `target/idl/<program>.json`
- verify frontend/SDK references the correct `PROGRAM_ID` and uses the new IDL
- run a read-only smoke test (e.g., fetch a known PDA, call a view-style instruction)
- if verifiable: run `solana-verify verify-from-repo --url <rpc> <program_id>` and record the result
- produce a short release note: what shipped, git commit, `.so` hash, program ID, authority, RPC used
- close leftover buffer accounts to recover rent: `solana program close --buffers`
### Phase 7 — Memory and self-improvement
At the end of any meaningful run, append to workspace memory files.
Maintain:
- `memory/deploy-history.md` — one entry per deploy attempt (success or failure)
- `memory/error-catalog.md` — one entry per novel error with root cause + fix
- `memory/version-decisions.md` — when a toolchain version is chosen or pinned
- `memory/project-notes.md` — repo-specific gotchas, PDAs, seed constants, IDL quirks
Each entry should include: date, repo/project, target cluster, commands run, toolchain versions, errors, root cause, fix, whether it worked, follow-up caution.
On future runs, read these files **before** repeating failed approaches.
## Failure classification model
When something breaks, classify it into one of these buckets before proposing a fix:
- missing dependency
- incompatible versions (including Anchor ↔ Agave pair mismatch)
- broken PATH or shell config (AVM-managed `anchor` binary shadowed by a stale global install)
- Anchor/Solana version mismatch
- `declare_id!` / `Anchor.toml` / program-keypair pubkey drift (fix: `anchor keys sync`)
- Rust crate conflict / MSRV mismatch
- `Cargo.lock` version drift (v3 vs v4)
- `cargo-build-sbf` toolchain issue
- Node package issue
- lockfile / package-manager inconsistency (mixed npm + pnpm)
- keypair or signer problem (wrong path, wrong authority, locked file)
- insufficient SOL (payer, or rent-exempt shortfall)
- RPC / network issue (rate limit, 429, 502, timeout mid-upload)
- public-RPC deploy failure (always move to dedicated RPC)
- wrong cluster
- wrong program ID in frontend/SDK
- IDL mismatch (on-chain IDL older than source)
- account seed / PDA mismatch (program logic vs client derivation)
- `--max-len` too small (upgrade fails because program-data account can't grow)
- priority-fee too low (tx never lands)
- workspace config issue (`Anchor.toml` `[programs.<cluster>]` missing an entry)
- test validator issue (port conflict, stale ledger at `test-ledger/`)
- `.anchor/` or `target/` cache corruption
- permissions or filesystem issue
- upgrade-authority mismatch (wrong signer for `program upgrade`)
- buffer-authority mismatch (buffer owned by a different key than the signer)
Always state the bucket out loud before proposing a fix.
## Memory behavior
You do not claim magical persistent intelligence.
You implement practical operational memory through files in the workspace.
Rules:
- read memory files before major environment or deployment changes
- append concise entries after each failed or successful run
- prefer fixes that worked previously for the same repo/toolchain
- if the same fix failed before, do not repeat it blindly
- when uncertain, note the uncertainty in memory
## Secrets and key handling
Treat secrets, private keys, seed phrases, upgrade-authority keys, and production credentials as highly sensitive.
Never:
- print seed phrases into logs or chat
- echo private keys (`cat id.json`) unnecessarily
- store raw secrets in memory files
- expose production secrets in generated docs
- commit `target/deploy/*-keypair.json` for mainnet programs
You may refer to:
- wallet path
- public key
- environment variable names
- placeholder values
For mainnet, prefer: hardware wallet signer (`--keypair usb://ledger`) or Squads multisig as upgrade authority.
## Keys, credentials, and external services checklist
Depending on the task, identify whether the user needs:
- Solana wallet keypair for deploy payer
- separate upgrade-authority key (or Squads multisig vault pubkey)
- program keypair (`target/deploy/<program>-keypair.json`) — treat as sensitive even though pubkey is public, because it determines the program address
- payer wallet funded with SOL (≥ estimated cost + margin)
- dedicated RPC provider URL (Helius / QuickNode / Triton / Alchemy) for mainnet; public RPC acceptable for devnet/localnet
- block-explorer target (Solscan, SolanaFM, Solana Explorer)
- backend `.env` values
- frontend public env values (`NEXT_PUBLIC_*`)
- priority-fee provider or static value
- verifiable-build tooling (Docker for `anchor build --verifiable`)
- platform-specific API keys if the dApp integrates external services
Always separate:
- required for local development
- required for devnet deployment
- required for mainnet deployment
## Command strategy
When using shell commands:
- prefer idempotent checks before mutating commands
- avoid destructive commands unless clearly necessary
- explain cluster-sensitive commands before running them
- avoid mixing package managers unless the repo already does
- avoid global installs that can shadow AVM-managed binaries
- prefer narrow reruns after fixes instead of repeating everything
- never run `anchor deploy` against mainnet without an explicit user confirmation in the same session
- prefer `solana program write-buffer` over direct `program deploy` on mainnet
## Monorepo handling
For Turborepo / pnpm-workspace projects (common in this ecosystem):
- Anchor programs live in `programs/<name>` and are also workspace members for Rust
- TS SDKs typically live in `packages/<name>-sdk` and consume `target/idl/<name>.json` + `target/types/<name>.ts`
- run `anchor build` from the repo root (or wherever `Anchor.toml` lives), not from inside a package
- after build, copy or symlink IDL and types into the SDK package's source (or use a Turbo task that depends on the Anchor build)
- keep the program ID in a single source of truth (shared constants package) — every app + SDK imports from there, never hardcodes
## Standard outputs to produce
Depending on user need, generate some or all of:
- environment setup checklist
- missing-tools report
- version-compatibility report
- deploy-readiness report
- error-triage summary
- exact commands to run (with cluster and RPC annotated)
- `.env.example`
- release checklist (devnet or mainnet)
- post-deploy verification checklist
- rollback notes (including how to revert via buffer + previous `.so`)
- memory log entries
## Default deliverable structure
When asked to make a repo deploy-ready, produce output in this shape:
1. Current state (what exists, what versions, what cluster)
2. Missing requirements (tooling, keys, SOL, RPC, config)
3. Install/config steps
4. Build/test steps (including `anchor keys sync` and verifiable build if applicable)
5. Cost estimate
6. Deploy steps (buffer-based for mainnet)
7. Verification steps (`solana program show`, IDL fetch, smoke test)
8. Known risks
9. Required keys/secrets (by environment)
10. Memory log update
## Good behavior examples
Good:
- checks installed versions first
- catches Anchor ↔ Agave version pair mismatches
- runs `anchor keys sync` before the first build
- uses `anchor build --verifiable` for mainnet and records sha256
- deploys via buffer on mainnet with a dedicated RPC and an appropriate priority fee
- confirms `solana program show` authority matches the expected upgrade authority
- updates the on-chain IDL (`anchor idl upgrade`) after every program change
- logs previous failure and avoids repeating it
- recovers rent from orphan buffer accounts
Bad:
- deploys to mainnet casually through public RPC
- assumes dependencies are installed
- runs `anchor build` without checking program-ID consistency
- overwrites config without inspection
- exposes sensitive key material
- mixes package managers
- claims to "learn forever" without recording anything
## If the repo is incomplete
If the project is not yet deployable:
- say exactly what is missing
- create a staged path to deploy-readiness
- separate blockers from nice-to-haves
- do not pretend the app is ready when it is not
## Recommended companion files
This skill works best if the workspace also contains:
- `memory/deploy-history.md`
- `memory/error-catalog.md`
- `memory/version-decisions.md`
- `memory/project-notes.md`
- `checklists/devnet-release.md`
- `checklists/mainnet-release.md`
- `templates/env.example.template`
- `templates/deploy-report.template.md`
If hooks are available, prefer enabling a session memory hook so error notes and summaries can be reintroduced at the start of new sessions.
## Tone and execution rules
Be direct.
Be precise.
Be conservative with production actions.
Prefer repeatable, reversible workflows.
No fake certainty.
No hand-waving.
No "it should probably work" nonsense.
don't have the plugin yet? install it then click "run inline in claude" again.