Hosting on the Internet Computer
How this site runs on ICP, step by step: one canister, a custom domain, and running costs under two dollars a year. If you have a static site, you can do the same in an afternoon.
What you get
- No server. The site lives in a canister, a small program on the ICP network. There is no machine, operating system or hosting plan to look after.
- Certified responses. The network signs every file it serves, and the HTTP gateway checks the signature before passing it on.
- Pay-as-you-go running costs. A canister pays for its storage and compute in cycles, bought with ICP. A small static site costs very little (see what it costs).
What you need
- A static site: HTML, CSS, images and scripts, with no server-side code.
- Node.js. On Windows, run everything below inside WSL with Ubuntu.
- A little ICP to convert into cycles. A few dollars' worth runs a small site for well over a year.
- Access to your domain's DNS, if you want your own domain.
1. Install the tools
sudo apt-get update
sudo apt-get install -y nodejs npm
sudo npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm
icp --version
2. Describe the canister
One file at the root of your project, icp.yaml, says there is one
canister that serves a folder of static files:
canisters:
- name: frontend
recipe:
type: "@dfinity/static-site@v0.3.3"
configuration:
dir: .icp-site
The static-site recipe uploads and certifies every file in that folder. No backend canister and no build tool are needed.
3. Stage only what you publish
Copy the public files into the staging folder, so that source art, scripts and
notes never go live. List your domains in .well-known/ic-domains:
ICP uses it to confirm the canister agrees to serve them.
rm -rf .icp-site
mkdir -p .icp-site/.well-known
cp index.html icp.html .icp-site/
cp -r assets .icp-site/
printf "example.com\nwww.example.com\n" > .icp-site/.well-known/ic-domains
Add .icp-site/ and .icp/ (local CLI state) to your .gitignore.
4. Try it locally
icp network start -d
icp deploy
# open the local URL it prints, then:
icp network stop
5. Create an identity and fund it
Your identity is the key that controls the canister. Create it once, keep its seed phrase in a password manager, and import that same identity on every machine you deploy from. A second identity is a different owner, and it cannot update a canister the first one created.
icp identity new my-site-deploy
icp identity default my-site-deploy
icp identity account-id --format ledger # send a little ICP here
icp cycles mint --icp 1 --environment ic
icp cycles balance --environment ic
Run the cycles commands from inside your project folder: they read
icp.yaml and fail with "project manifest not found" anywhere
else.
6. Deploy to the network
icp deploy --environment ic frontend
It prints your canister ID, and the site is live at
https://<canister-id>.icp.net/. This site's canister is
ekyqj-aaaaa-aaaao-bbksq-cai.
7. Point your domain at it
Each domain needs three DNS records. For www:
| Type | Host | Value |
|---|---|---|
| CNAME | www | www.example.com.icp1.io |
| TXT | _canister-id.www | your canister ID |
| CNAME | _acme-challenge.www | _acme-challenge.www.example.com.icp2.io |
Once DNS has spread, register the domain and watch for its certificate:
curl -sL "https://icp.net/custom-domains/v1/www.example.com/validate"
curl -sL -X POST "https://icp.net/custom-domains/v1/www.example.com"
curl -sL "https://icp.net/custom-domains/v1/www.example.com" # wait for "registered"
The bare domain (example.com) needs a DNS provider that allows a
CNAME at the root, such as Cloudflare with the records set to DNS only. Some
registrars' DNS, GoDaddy's included, cannot do this, so a subdomain is the
quickest start.
8. Keep it running
A canister spends cycles every day just to exist, mostly for storage. If it runs out, it freezes, and a frozen canister is eventually deleted with the site in it. Checking the balance is the one piece of upkeep you cannot skip.
icp canister status <canister-id> --environment ic
Read three fields from the report and work out the runway:
reserve = idle cycles burned per day × freezing threshold (in days)
runway = (cycles − reserve) ÷ idle cycles burned per day
This site's figures, from September 2026:
| Balance | 1.49 trillion cycles |
|---|---|
| Burn | 3.5 billion cycles a day, about 1.3 trillion a year |
| Cost of a year | about $1.70 (a trillion cycles costs about $1.35) |
| Runway | about 390 days, after a 30-day freezing reserve |
Top up when the runway drops below about 90 days, and look again after every deploy: if the memory size climbs each time, old files are piling up rather than being replaced.
icp cycles mint --cycles 1t --environment ic
icp canister top-up <canister-id> --amount 1t --environment ic
Keep your keys out of the repo
The seed phrase, identity password and any exported key file belong in a password manager, never in the project folder, a script or a chat. The canister ID and your principal are public; the key that controls them is not.
Further reading
Want your site on ICP without doing it yourself? hello@roamrock.com