suideveloper
All questions
Packages

How do I publish a Move package to Sui?

November 13, 2026

Run sui client publish from your package's root directory (the folder with Move.toml). The CLI compiles the package, checks dependencies against their on-chain bytecode, sends a publish transaction from your active address on your active network, and prints the new package ID, the UpgradeCap that controls future upgrades, and any objects your init functions created. It also writes the publication details to Published.toml.

Before you publish

You need the Sui CLI, an active address with SUI for gas, and a package that builds:

Terminal
sui client active-env        # which network you're on
sui client active-address    # which address pays
sui client gas               # coins available for gas
sui move build
sui move test

On testnet, get SUI from the faucet. A minimal Move.toml for the current package system is just:

TOML
[package]
name = "counter"
edition = "2024"

std and sui are included automatically, so don't list them. Modules declare the package's own name as their address, as in module counter::counter;. The publish step assigns the real address.

Publish

Terminal
cd counter
sui client publish

You don't need --gas-budget. When you leave it out, the CLI dry-runs the transaction to estimate one. To check cost and output without spending anything, run:

Terminal
sui client publish --dry-run

In the output, look at Object Changes:

  • Published Objects: the PackageID is your package ID.
  • Created Objects: one object of type 0x2::package::UpgradeCap, plus anything init created (shared objects, admin caps, a Publisher, and so on).

Save both IDs. See how to find a package ID if you lost them.

Published.toml

Since Sui CLI 1.63, the package manager writes a Published.toml next to Move.toml with an entry per network: published-at, original-id, version, upgrade-capability, the toolchain version and the build config. Commit it to source control. Other packages use it to link against yours, and sui client upgrade reads the UpgradeCap from it.

mainnet and testnet are built-in environments. For localnet or devnet, use sui client test-publish instead, which records addresses in an ephemeral Pub.<env>.toml file that you shouldn't commit:

Terminal
sui client test-publish --build-env testnet

If you see Your active environment is not present in Move.toml, this is usually why.

Calling it

Terminal
sui client call --package 0x<PACKAGE_ID> --module counter --function increment --args 0x<COUNTER_ID>

More on this in calling Move functions from the CLI.

Gotchas

  • Publishing can't be undone. The package is an immutable object on chain. You can upgrade it with the UpgradeCap, but old versions stay callable.
  • init runs once, at first publish only. Upgrades don't re-run it.
  • Protect the UpgradeCap. Whoever holds it can change your code. For production, hold it in a multisig, restrict its policy, or make the package immutable. See UpgradeCap and upgrade policies.
  • Multisig publishing: sui client publish --serialize-unsigned-transaction prints transaction bytes that signers can sign elsewhere. Submit the result with sui client execute-signed-tx.
  • Verify afterwards: sui client verify-source rebuilds your package with the toolchain it was published with and compares the result to the on-chain bytecode.

Sources