Deployment & Upgrades In Detail
Shipping a Solana program is not "upload a binary and walk away." It is a lifecycle: choose a program identity, fund rent for bytecode storage, stage ELF through the upgradeable loader, prove what you shipped, decide who may change it later, and plan what happens when a release goes wrong.
This page is the conceptual map for the section. Use it to place deploy flow, the upgradeable loader, upgrades, immutability, governance-gated authority, verifiable builds, and rollbacks on one continuum before you follow the focused recipe pages.
Summary
On modern Solana tooling (Agave 4.1.1 / Solana CLI 3.0.10), programs are almost always loaded with the BPF Upgradeable Loader. That model splits concerns:
- A program account at the program ID is the stable, executable address clients call.
- A program data account holds the ELF (SBF) bytes and the upgrade authority pubkey (or none).
- A buffer account stages multi-step uploads so large deploys can resume and rent can be reclaimed on failure.
Deploy creates (or fills) those accounts from a built .so. Upgrade reuses the same program ID and replaces the ELF under authority control. Immutable means authority is permanently cleared so no further upgrade can succeed. Governance-gated means authority is a multisig or DAO vault, not a developer laptop key. Verifiable builds close the trust gap between public source and on-chain bytes. Rollback is operational: redeploy a previously attested artifact and re-verify the hash.
Account state your program owns (PDAs, vaults, config) is separate from program bytecode. Upgrading code does not rewrite user data layouts for you. Compatibility and migration are your responsibility.
Foundations
What "deployment" means on Solana
Deployment is the process of putting executable bytecode on-chain so the runtime can invoke your program when a transaction lists your program ID. Clients such as Anchor 0.32.1, the Solana CLI, and @solana/kit 7.0.0 all target the same loader contracts: fund accounts, write bytes, mark executable, record who may upgrade.
Unlike traditional server deploys, there is no SSH session and no hidden process manager. The artifact is public, the authority is on-chain, and every future change is another signed transaction (or a hard stop if authority is gone).
Program ID is product identity
The program ID is the pubkey of the program keypair (or the address you pass with --program-id). It must match declare_id! (Anchor) or your clients' hard-coded address. Changing program ID is a new deployment: clients, explorers, and PDA derivations that include the program id all move. Prefer same-ID upgrades when logic changes and state should stay under existing addresses.
The upgradeable loader account model
| Account | Role |
|---|---|
| Program account | Executable proxy at the program ID; points at ProgramData |
| ProgramData | ELF bytes + upgrade authority + last deploy metadata |
| Buffer | Temporary upload target for chunked writes during deploy/upgrade |
solana program show <PROGRAM_ID> is the day-one inspection tool: data length, authority, ProgramData address, and last deploy slot. Treat that output as the control plane for release ops.
Upgrade authority is the real security boundary
Whoever can sign as upgrade authority can replace production logic for every user of that program ID. That is stronger than a website deploy and closer to root on a shared binary. Hot wallets are fine for localnet and early devnet. Before meaningful TVL or public reliance, move authority to a controlled holder (typically Squads multisig or DAO execution path) or deliberately revoke it.
Losing the authority key without a recovery path freezes upgrades forever. That can be intentional (immutability) or accidental (ops failure). Design for the outcome you want.
Toolchain pins for this guide
Treat versions as a team contract. This guide assumes Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, Rust 1.91.1, and @solana/kit 7.0.0. Pin CLI, platform-tools, and Anchor like you pin Cargo.lock. Mismatched toolchains are a common cause of "it built on my laptop" hash mismatches under solana-verify.
Mechanics
Think of release ops as one pipeline with a few optional branches at the end: build, stage, land, verify, then either keep upgradeable (governed or not), freeze, or prepare to roll back.
End-to-end deploy and upgrade pipeline
Source (Rust / Anchor 0.32.1)
|
| cargo build-sbf / anchor build
| optional: solana-verify build (reproducible)
v
Artifact: target/deploy/my_program.so
Identity: program keypair / declare_id!
|
| solana program write-buffer (optional explicit buffer)
| or solana program deploy / anchor deploy
v
+---------------------------+
| Upgradeable Loader |
| buffer <- ELF chunks |
| ProgramData <- buffer |
| program account links |
+---------------------------+
|
| authority signs deploy/upgrade
v
On-chain executable at PROGRAM_ID
|
| solana-verify get-program-hash
| solana program show
v
Attest: {commit, hash, program_id, cluster}
|
+--> keep authority (dev / hot path)
+--> transfer authority (Squads / DAO)
+--> set authority --final (immutable)
|
| if bad release and still upgradeable:
v
Redeploy last known-good .so (rollback)
1. Build the artifact
anchor build wraps cargo build-sbf and emits IDL plus target/deploy/*.so for Anchor workspaces. Native crates use the SBF toolchain directly. Build on the pinned Agave/CLI stack so the ELF matches what validators expect.
For release candidates, prefer a reproducible path (solana-verify build in a controlled container) rather than a developer host binary alone. Host builds are fine for iteration; they are weak evidence for mainnet attestation.
2. First deploy
solana program deploy path/to/program.so --program-id <keypair> funds rent for ProgramData proportional to bytecode size, writes via a buffer path under the hood when needed, and sets the deployer (or configured authority) as upgrade authority by default.
anchor deploy does the same within workspace config (Anchor.toml cluster and wallet). Always confirm cluster with solana config get before mainnet. Use --dry-run when you need a cost preview.
Failed mid-deploy leaves buffer rent behind. Close abandoned buffers or resume from a known buffer address. Details and recipes live in Deploying Programs and Deployment Basics.
3. Upgrade (same program ID)
Upgrade is not a separate magical opcode for product teams: you deploy a new .so targeting the existing program ID while holding upgrade authority. The loader replaces ProgramData contents; the program account address stays fixed. Clients keep calling the same pubkey. New transactions execute the new ELF after the upgrade lands.
Safe upgrades are a process, not a CLI flag:
- Tag the commit and archive the
.so+ hash. - Run full tests (
anchor test, negatives, migration paths). - Rehearse on devnet with the same pipeline.
- Verify hash post-deploy.
- Execute mainnet via governance if authority is gated.
- Monitor and keep the previous artifact ready.
State layout breaks are the classic silent killer. If accounts gain fields or change meaning, ship versioned layouts and migration instructions. See Program Upgrades.
4. Verifiable builds close the source-to-chain gap
Users and auditors should not trust a tweet that "this is the code." solana-verify rebuilds under a controlled environment, hashes the executable, and compares against on-chain program data via RPC.
Minimum release evidence:
| Field | Why it matters |
|---|---|
| Commit SHA | Source identity |
| Program ID | On-chain identity |
| Cluster | mainnet vs devnet |
| Executable / program hash | Byte-level attestation |
| Toolchain pins | Reproducibility |
Gate CI so deploy jobs only ship the verified artifact. After deploy, re-fetch the program hash. Local match before upload is not enough if the wrong file was passed to program deploy.
5. Governance-gated upgrades
Transferring upgrade authority to a Squads multisig (or DAO-controlled vault) turns bytecode change into a proposal: memo, hash, CI links, risk notes, and M-of-N signatures. Timelocks give the community time to react before execution.
Typical split:
| Environment | Authority pattern |
|---|---|
| Local / CI | Ephemeral or team hot key |
| Devnet | Team key or light multisig |
| Mainnet pre-TVL | Multisig before public risk |
| Mainnet mature | Multisig + policy, or path to immutability |
Never leave a "hidden" hot-wallet authority for emergencies while marketing multisig security. Explorers and program show will reveal the real authority. See Governance-Gated Upgrades.
6. Making programs immutable
solana program set-upgrade-authority <PROGRAM_ID> --final clears authority permanently. After that, no upgrade instruction can change the ELF. Trust in code stability rises; ability to patch critical bugs without a new program ID falls to zero.
Immutability is a product decision with ops consequences:
- Do it only after audit, bake period, and verified hash attestation.
- Pre-plan migration tooling if a future bug would require a new program and state move.
- Prefer multisig-executed
--finalso a single compromised laptop cannot freeze the wrong artifact.
Recipe detail: Making Programs Immutable.
7. Rollbacks and incident response
The runtime does not keep a stack of previous ELFs for one-click undo. Rollback means:
- Detect and contain (pause risky UI flows if needed).
- Dump current on-chain bytes for forensics (
solana program dump). - Redeploy the last known-good
.sounder the same program ID (if still upgradeable). - Verify hash and smoke-test critical paths.
- Communicate slot, signature, impact, and next steps.
If the program is already immutable, "rollback" becomes migrate users and liquidity to a new program ID, which is far more expensive. Archive every release artifact and hash in CI so rollback is possible. Full runbook patterns: Rollbacks & Incident Response.
Advanced
Same-ID upgrade vs new program migration
| Path | Keep program ID | Keep PDAs | When to use |
|---|---|---|---|
| Upgrade ELF | Yes | Yes if seeds unchanged | Compatible logic/layout fixes |
| New program + migrate | No | Only with careful re-derive / transfer | Breaking trust model, freeze, irreversible redesign |
| Feature flags in data | Yes | Yes | Toggle behavior when code already supports it |
Feature flags do not replace upgrades when the bug is in shipped code. They only reduce how often you touch bytecode for optional surfaces.
Rent scales with ProgramData size (roughly .so size). Close abandoned buffers so failed deploys do not leave rent stranded. Priority fees help landing under congestion but are secondary to rent for large programs.
Authority lifecycle you can defend in an audit
- Deploy with team authority on devnet; iterate quickly.
- Lock mainnet authority to multisig before TVL.
- Require verifiable hash + devnet evidence on every proposal.
- Archive artifacts for rollback.
- Optionally revoke authority after a long stable period if immutability is the product promise.
Auditors check program show, proposal history, and published hashes. Align marketing with the actual authority state.
Clients, monitoring, and drift
Upgrades keep the program ID but can break IDL assumptions. Pin client IDLs and Codama/kit-generated types to the deployed version. Coordinate cutovers when instruction layouts change. @solana/kit 7.0.0 does not change loader semantics; release hygiene is operational.
Watch for unexpected upgrades or authority changes, hash drift from last attestation, error spikes after a deploy slot, and stuck governance during incidents. Monitoring without retained artifacts still leaves rollback blind.
Common Misconceptions
Misconception: Deploy is one-time; upgrades are rare special cases.
Most production systems stay upgradeable for months. Design authority and verification on day one.
Misconception: The program account holds all the bytecode by itself.
ProgramData holds ELF and authority. The program account is the stable executable handle.
Misconception: Multisig makes upgrades automatically safe.
It raises the bar for unilateral change. Tests, verifiable builds, and devnet rehearsal still matter. Signers can approve a bad proposal.
Misconception: Verifiable builds are only marketing for open source.
They also stop you from deploying the wrong artifact and give incident responders ground truth for on-chain bytes.
Misconception: Immutable programs can still be hotfixed by the original team.
--final is final at the loader. After that you need a new program and migration (unless you disclosed an indirection design).
Misconception: A Git tag alone guarantees rollback.
Archive the release .so and hash (or prove a reproducible rebuild of that tag). Git without the binary is not a rollback pack.
Misconception: Upgrading code migrates account data.
Only your instructions migrate state. New layouts on old accounts can brick paths or misread funds.
Misconception: Devnet deploy proves mainnet governance execution.
Devnet proves bytecode and smoke tests. Multisig UX, thresholds, and timelocks still need rehearsal.
Misconception: A hidden hot emergency key is harmless.
If it remains upgrade authority, it is the security model.
FAQs
What is the upgradeable loader?
The on-chain program that owns upgradeable program accounts, stores ELF in ProgramData, and enforces upgrade authority on deploy and upgrade.
Does an upgrade change my program ID?
No. Same program pubkey; only executable bytes and related loader metadata change.
What is a buffer account?
A temporary account for ELF chunks during deploy or upgrade so large uploads can resume and failed attempts can reclaim rent.
How do I see who can upgrade a program?
solana program show <PROGRAM_ID> and read Authority. None means immutable.
When should I transfer authority to multisig?
Before mainnet capital or public dependency. Keep hot keys for local and early devnet only.
What does solana-verify prove?
That a controlled rebuild matches the on-chain executable hash when source, features, and locks match. It does not prove economic safety of the logic.
Can I undo set-upgrade-authority --final?
No. Plan migration to a new program ID if you must change logic after immutability.
How do mainnet rollbacks work?
If still upgradeable, redeploy a retained known-good .so to the same program ID via the authority path, then re-verify the hash. If immutable, migrate.
Is anchor deploy different from solana program deploy?
anchor deploy wraps workspace config and the same loader workflow. Native programs typically call the Solana CLI directly.
Why did my local hash not match CI?
Toolchain drift, unlocked deps, feature flags, or host vs container builds. Gate releases on solana-verify from a pinned environment.
Do clients need to change after an upgrade?
Not if instruction and account contracts stay compatible. Otherwise ship coordinated client/IDL updates with the upgrade.
Where should I go next in this section?
Start with Deployment Basics and Deploying Programs, then Program Upgrades. Add Governance-Gated Upgrades, Making Programs Immutable, and Rollbacks & Incident Response for production policy.
Related
- Deploying Programs - buffers,
program deploy, and deploy costs - Deployment Basics - loader model, authority, and first inspections
- Program Upgrades - same-ID upgrades and safe release checklist
- Governance-Gated Upgrades - multisig and DAO-controlled authority
- Making Programs Immutable - revoking upgrade authority for good
- Rollbacks & Incident Response - bad deploys, dumps, and recovery
Stack versions: This page was written for Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, Rust 1.91.1, and @solana/kit 7.0.0.