Overview
After you have built a canister, you can deploy it either on the local canister execution environment, a sandbox environment such as the Motoko playground or on the mainnet. Your canister must be deployed before you can make calls to it and execute methods.
Step 1: Open a new terminal and navigate to your project directory.
Step 2: Start the local canister execution environment, if necessary.
dfx start
Step 3: Verify the canisters you'd like to deploy are configured in the project's
dfx.json
file.
Need to create a project? Check out the Hello, world! sample project.
An example dfx.json
file can be found in the default project template document.
Step 4: Deploy all of the canisters within your project.
To deploy locally, use the command:
dfx deploy
To deploy to the Motoko playground, use the command:
dfx deploy --playground
To deploy to the mainnet, use the command:
dfx deploy --network ic
Deploying canisters to the mainnet will cost cycles. Learn more about cycles and how to acquire them.
This command deploys all canisters configured in your dfx.json
file. To deploy just one canister, specify the canister's name:
dfx deploy hello_backend ## Deploy locally
dfx deploy hello_backend --playground ## Deploy to the playground
dfx deploy hello_backend --network ic ## Deploy to the mainnet
These commands both deploy to the local network since they do not include a --network
flag.
For more information on deploying to the local or mainnet networks:
Use a custom Motoko version with dfx deploy
To use a custom Motoko version with dfx deploy
, export the following environment variable that indicates which Motoko base version you'd like dfx
to use:
DFX_MOC_PATH="$(vessel bin)/moc" dfx deploy
Setting a canister's init arguments
You can set a canister's init arguments when the canister is deployed by passing the --argument
flag in either the dfx install
or dfx deploy
commands:
dfx canister install <CANISTER_NAME> --argument "(arg in candid)"
dfx deploy <CANISTER_NAME> --argument "(arg in candid)"
If several arguments should be used, an argument file can be defined with the --argument-file
flag instead:
dfx deploy <CANISTER_NAME> --argument-file file.txt
Alternatively, init arguments can be set in dfx.json
in dfx
versions v0.17.0
and newer:
"canisters": {
"hello_backend": {
"candid": "src/hello_backend/hello_backend.did",
"package": "hello_backend",
"type": "rust",
"init_arg": "(arg in candid)"
},
}
If an init argument is set in dfx.json
and set with the CLI command, the argument set in the CLI command is used.
Setting tasks to execute once a canister has been deployed
For certain workflows, it may be important to have a canister execute a task or call as soon as the canister is deployed or started. For this workflow, using timers can be useful.
Here is an example:
- Motoko
- Rust
- TypeScript
- Python
This example uses a global timer that gets called immediately after the canister starts:
system func timer(setGlobalTimer : Nat64 -> ()) : async () {
let next = Nat64.fromIntWrap(Time.now()) + 20_000_000_000;
setGlobalTimer(next); // absolute time in nanoseconds
print("Tick!");
}
You can learn more in the Motoko Timer library documentation.
This example creates a periodic timer that gets called immediately after the canister starts:
#[ic_cdk::init]
fn init(timer_interval_secs: u64) {
let interval = std::time::Duration::from_secs(timer_interval_secs);
ic_cdk::println!("Starting a periodic task with interval {interval:?}");
ic_cdk_timers::set_timer_interval(interval, || {
COUNTER.fetch_add(1, Ordering::Relaxed);
});
}
This example creates a timer function that must be manually called once a canister starts:
import {
blob,
bool,
Canister,
Duration,
ic,
int8,
query,
Record,
serialize,
text,
TimerId,
update,
Void
} from 'azle';
import { managementCanister } from 'azle/canisters/management';
const StatusReport = Record({
single: bool,
inline: int8,
capture: text,
repeat: int8,
singleCrossCanister: blob,
repeatCrossCanister: blob
});
type StatusReport = typeof StatusReport.tsType;
const TimerIds = Record({
single: TimerId,
inline: TimerId,
capture: TimerId,
repeat: TimerId,
singleCrossCanister: TimerId,
repeatCrossCanister: TimerId
});
type TimerIds = typeof TimerIds.tsType;
let statusReport: StatusReport = {
single: false,
inline: 0,
capture: '',
repeat: 0,
singleCrossCanister: Uint8Array.from([]),
repeatCrossCanister: Uint8Array.from([])
};
export default Canister({
clearTimer: update([TimerId], Void, (timerId) => {
ic.clearTimer(timerId);
console.log(`timer ${timerId} cancelled`);
}),
setTimers: update([Duration, Duration], TimerIds, (delay, interval) => {
const capturedValue = '🚩';
const singleId = ic.setTimer(delay, oneTimeTimerCallback);
const inlineId = ic.setTimer(delay, () => {
statusReport.inline = 1;
console.log('Inline timer called');
});
const captureId = ic.setTimer(delay, () => {
statusReport.capture = capturedValue;
console.log(`Timer captured value ${capturedValue}`);
});
const repeatId = ic.setTimerInterval(interval, () => {
statusReport.repeat++;
console.log(`Repeating timer. Call ${statusReport.repeat}`);
});
const singleCrossCanisterId = ic.setTimer(
delay,
singleCrossCanisterTimerCallback
);
const repeatCrossCanisterId = ic.setTimerInterval(
interval,
repeatCrossCanisterTimerCallback
);
return {
single: singleId,
inline: inlineId,
capture: captureId,
repeat: repeatId,
singleCrossCanister: singleCrossCanisterId,
repeatCrossCanister: repeatCrossCanisterId
};
}),
statusReport: query([], StatusReport, () => {
return statusReport;
})
});
function oneTimeTimerCallback() {
statusReport.single = true;
console.log('oneTimeTimerCallback called');
}
async function singleCrossCanisterTimerCallback() {
console.log('singleCrossCanisterTimerCallback');
statusReport.singleCrossCanister = await getRandomness();
}
async function repeatCrossCanisterTimerCallback() {
console.log('repeatCrossCanisterTimerCallback');
statusReport.repeatCrossCanister = Uint8Array.from([
...statusReport.repeatCrossCanister,
...(await getRandomness())
]);
}
async function getRandomness(): Promise<blob> {
if (process.env.AZLE_TEST_FETCH === 'true') {
const response = await fetch(`icp://aaaaa-aa/raw_rand`);
const responseJson = await response.json();
return responseJson;
} else {
return await ic.call(managementCanister.raw_rand);
}
}
This example creates a timer function that must be manually called once a cansiter starts:
from kybra import (
Duration,
ic,
TimerId,
update,
)
@update
def set_timer(delay: Duration) -> TimerId:
return ic.set_timer(delay, timer_callback)
def timer_callback():
ic.print("timer_callback")