Skip to main content
Canisters are the smart contracts of the Internet Computer. They combine code (WebAssembly) and state, running in a secure, isolated environment with the ability to respond to messages, store data persistently, and interact with other canisters and external systems.

What is a Canister?

A canister is an evolved form of a smart contract with several key characteristics:

WebAssembly Execution

Runs compiled WebAssembly code for high performance

Persistent State

Maintains state across executions with automatic persistence

Isolated Execution

Runs in sandboxed environment for security

Cycles-Powered

Uses cycles for computation and storage costs
The canister execution environment is implemented in rs/execution_environment/ and provides the runtime for all canister operations.

Canister Lifecycle

States and Transitions

A canister progresses through several states during its lifetime:
The canister is active and can:
  • Process incoming messages
  • Execute scheduled tasks
  • Send outgoing messages
  • Access its persistent state
  • Consume cycles for operations

Lifecycle Management

1

Creation

Create a new canister with create_canister:
// From rs/execution_environment/src/execution_environment.rs
// Creates canister with unique ID and initial cycles
2

Installation

Install WebAssembly code with install_code:
// Modes: install, reinstall, upgrade
// - install: First time code installation
// - reinstall: Replace code and reset state
// - upgrade: Replace code while preserving state
3

Execution

Canister processes messages and executes tasks:
// Update calls: Modify state
// Query calls: Read-only, fast responses
// Heartbeats: Periodic scheduled execution
4

Upgrade

Upgrade to new code version while preserving state:
// Pre-upgrade hook: Serialize state
// Install new code
// Post-upgrade hook: Deserialize state
5

Deletion

Remove the canister with delete_canister:
// Canister must be stopped first
// Remaining cycles returned to controller

Canister Types

Application Canisters

User-deployed canisters that implement dapp logic: Characteristics:
  • Created by users or other canisters
  • Controlled by principals (users or other canisters)
  • Subject to cycles consumption
  • Can be upgraded by controllers
  • Implement arbitrary business logic
Example Use Cases:
  • DeFi protocols
  • Social networks
  • DAOs and governance systems
  • Gaming applications
  • Data storage services

System Canisters

Special canisters that provide core IC functionality:

NNS Canisters

Governance, Registry, Ledger, Root, and other NNS components

Management Canister

Virtual canister (ic00) for administrative operations

SNS Canisters

Service Nervous System components for dapp governance

System Subnet Canisters

Specialized canisters on system subnets (Cycles Minting, etc.)
Key Differences:
  • Often have privileged operations
  • May have special cycle charging rules
  • Cannot always be upgraded through normal means
  • Integrated with IC protocol

Execution Model

Message Processing

Canisters respond to different types of messages:
Modify canister state and are committed to blockchain:
// From rs/execution_environment/src/execution/call_or_task.rs
// - Process through consensus
// - Can modify state
// - Generate replies
// - Trigger inter-canister calls
Properties:
  • 2-4 second latency (consensus delay)
  • Certified responses
  • Consumes cycles
  • Can call other canisters
Read-only operations that don’t modify state:
// From rs/execution_environment/src/query_handler/
// - Bypass consensus
// - Read-only access to state
// - Fast responses (~200ms)
// - Lower cost
Properties:
  • Sub-second latency
  • No state changes committed
  • Lower cycle cost
  • Cannot call other canisters
Asynchronous calls between canisters:
// Caller makes async call
// Callee processes and responds
// Caller receives callback
// State changes committed at each step
Properties:
  • Asynchronous execution model
  • Multiple round trips possible
  • Each step goes through consensus
  • Cycles transferred for computation
Scheduled periodic execution:
// Heartbeat: Called every subnet round
// Timer: Called at specified intervals
// No external trigger needed
Properties:
  • Automatic invocation
  • Useful for maintenance tasks
  • Consumes cycles on each execution
  • Must complete within instruction limits

Execution Limits

Canisters operate within defined resource limits:
ResourceLimitPurpose
Instructions~5-20 billion per messagePrevent infinite loops and DoS
Message Size2 MBLimit network bandwidth usage
Wasm Memory4 GB (32-bit) or more (64-bit)Canister heap space
Stable MemoryUp to 400 GBPersistent storage across upgrades
Call Stack500 levelsPrevent deep recursion
Exceeding execution limits will cause message execution to fail and state changes to be rolled back.

Sandboxed Execution

Canisters run in isolated sandboxes for security:
Replica Process
├── Execution Environment
│   ├── Canister Sandbox Manager (rs/canister_sandbox/)
│   │   ├── Sandbox Process 1 (Canister A)
│   │   ├── Sandbox Process 2 (Canister B)
│   │   └── Sandbox Process N (Canister N)
│   └── Message Routing
└── State Manager
Sandbox Features:

Process Isolation

Each canister runs in a separate OS process

Memory Protection

Canisters cannot access each other’s memory

Resource Limits

CPU and memory limits enforced by OS

Syscall Filtering

Restricted system call access via seccomp
// rs/canister_sandbox/src/replica_controller/
// - sandboxed_execution_controller.rs: Manages sandboxes
// - sandbox_process_eviction.rs: Process lifecycle
// - launch_as_process.rs: Sandbox process creation

State Management

Wasm Memory

Canister heap memory (volatile):
// Automatically saved/restored during upgrades
// Limited by WebAssembly memory constraints
// Fast access for active data

Stable Memory

Persistent storage across upgrades:
// Explicitly managed by canister code
// Survives code upgrades
// Up to 400 GB capacity
// Uses stable_read/stable_write System API
Upgrade Pattern:
1

Pre-Upgrade Hook

Serialize important state to stable memory:
#[pre_upgrade]
fn pre_upgrade() {
    // Save state to stable memory
    let state = get_state();
    stable_write(0, &serialize(state));
}
2

Code Replacement

New WebAssembly code is installed
3

Post-Upgrade Hook

Deserialize state from stable memory:
#[post_upgrade]
fn post_upgrade() {
    // Restore state from stable memory
    let bytes = stable_read(0, size);
    set_state(deserialize(&bytes));
}
Use stable memory for data that must persist across upgrades. Wasm memory is faster but is reset on reinstall.

Cycles and Resource Costs

Canisters pay for resources using cycles: Cost Factors:
  • Computation: Instructions executed
  • Storage: Bytes stored over time
  • Network: Message sizes
  • Special Operations: HTTPS outcalls, threshold signatures, etc.
Cycle Management:
// Check canister balance
canister_balance() -> u64

// Accept cycles from caller
msg_cycles_accept(amount) -> u64

// Send cycles with call
call_with_cycles(canister_id, method, args, cycles)
If a canister runs out of cycles, it will be uninstalled and its state will be deleted. Monitor cycle balances carefully.

System API

Canisters interact with the IC through the System API:
msg_caller() -> Principal       // Caller's principal
msg_arg_data_size() -> u32     // Argument size
msg_arg_data_copy()            // Read arguments
msg_reply()                    // Send reply
msg_reject()                   // Reject call
canister_cycle_balance() -> u64        // Current balance
canister_status() -> CanisterStatus    // Running/Stopped/etc
canister_version() -> u64              // Upgrade version
call_new()                     // Initialize call
call_data_append()             // Add arguments
call_cycles_add()              // Attach cycles
call_perform()                 // Execute call
time() -> u64                  // Current time (nanoseconds)
performance_counter() -> u64    // Performance metric
global_timer_set() -> u64      // Schedule timer
stable_size() -> u64           // Stable memory pages
stable_grow(pages) -> i64      // Allocate pages
stable_read(offset, size)      // Read from stable memory
stable_write(offset, data)     // Write to stable memory
stable64_*()                   // 64-bit stable memory API

Best Practices

Design for Upgrades

  • Use stable memory for critical state
  • Implement pre/post upgrade hooks
  • Version your data structures
  • Test upgrade paths thoroughly

Manage Resources

  • Monitor cycle balance
  • Set up cycle top-up mechanisms
  • Optimize instruction usage
  • Use query calls when possible

Handle Errors

  • Check return values from System API
  • Handle inter-canister call failures
  • Implement retry logic
  • Validate input data

Security First

  • Validate all inputs
  • Check caller identity
  • Use access control
  • Audit upgrade permissions

Source Code Reference

// rs/execution_environment/src/
// - execution_environment.rs: Core execution logic
// - hypervisor.rs: WebAssembly execution
// - scheduler.rs: Message scheduling
// - canister_manager.rs: Lifecycle management
Key Files:
  • rs/execution_environment/src/execution_environment.rs: Main execution logic
  • rs/execution_environment/src/hypervisor.rs: Wasm execution and System API
  • rs/canister_sandbox/src/replica_controller/: Sandbox management
  • rs/replicated_state/src/canister_state/: Canister state structures

State Management

Learn about state synchronization and persistence

Network Nervous System

Understand IC governance and system canisters

Cryptography

Explore IC’s cryptographic features

Service Nervous System

Build decentralized dapp governance

Build docs developers (and LLMs) love