A Walrus Site is made up of 4 main components that work together: the site's files stored on Walrus, a [Sui object](https://docs.sui.io/guides/developer/objects/object-model) that indexes those files as resources, the [`site-builder` CLI](/docs/sites/getting-started/installing-the-site-builder) that publishes and updates the site, and a portal that serves the site to browsers.

Each section below covers one component on its own. To follow all four through a single page load, read [Technical Overview](/docs/sites/introduction/technical-overview). To skip the theory and deploy something, go straight to [Publishing Your First Site](/docs/sites/getting-started/publishing-your-first-site), or browse the full guide set from [Walrus Sites](/docs/sites).

## Site files on Walrus

Your site's static assets (HTML, CSS, JavaScript, images, fonts, and so on) are stored as blobs on Walrus. When you deploy a site, the `site-builder` uploads your assets to Walrus by encoding multiple files into a single storage entity called a [quilt](/docs/system-overview/quilt), a unique feature designed to reduce upload cost and improve upload speed. Each file in the quilt is assigned a `QuiltPatchID`.

## The Sui site object

Each Walrus Site corresponds to a single object on Sui, defined by the Move smart contract in the [`move/walrus_site`](https://github.com/MystenLabs/walrus-sites/tree/main/move/walrus_site) directory of the repository. Unlike storing a single blob directly on Walrus, a Walrus Site bundles all its files into a quilt and represents the entire site as one Sui object. At its core, a site object is a basic structure:

```move
struct Site has key, store {
    id: UID,
    name: String,
}
```

Each resource in the site is attached to this object as a dynamic field of type `Resource`:

```move
struct Resource has store, drop {
    path: String,
    blob_id: u256,
    // ...headers and other metadata
}
```

The `path` field corresponds to the resource's URL path (for example, `/index.html`), and `blob_id` is the Walrus identifier for the resource's content. The site object uses each file's `QuiltPatchID` to locate and retrieve it from the quilt. Together, the site object and its dynamic fields form an onchain index that maps every resource path in your site to its content on Walrus. Because Walrus is a decentralized storage network, there is no single server that can take your content offline.

Because the site object is a standard Sui object, it has a single owner. The wallet that deployed the site owns the object and is the only wallet that can update or destroy it. Ownership can also be transferred, meaning you can move a site to a different wallet if needed. For more detail on how Sui handles object ownership, see the [Sui object model](https://docs.sui.io/guides/developer/objects/object-model) documentation. You can also assign a [SuiNS](https://suins.io/) name to the object, giving your site a human-readable domain name.

> **Info**
>
> Blobs are stored for a fixed number of epochs. On Mainnet, each epoch lasts 14 days. On Testnet, each epoch lasts 1 day. The maximum storage duration is 53 epochs.
## The `site-builder` CLI

The `site-builder` is a Rust CLI tool that creates and manages Walrus Sites. It takes your site's build output directory as input and handles uploading files to Walrus and writing the onchain index to Sui.

The primary command is [`deploy`](/docs/sites/getting-started/using-the-site-builder#deploy), which both publishes new sites and updates existing ones:

```sh
site-builder deploy --epochs <NUMBER> <DIRECTORY>
```

The `site-builder` requires a [`sites-config.yaml` configuration file](/docs/sites/getting-started/using-the-site-builder) that specifies the Sui package ID for the Walrus Sites contract and your network context.

When you run `deploy` for the first time, it publishes a new site and writes the resulting Sui object ID to the `object_id` field in `ws-resources.json`. On subsequent runs, `deploy` reads that object ID and updates the existing site instead of creating a new one.

## `ws-resources.json`

[`ws-resources.json`](/docs/sites/configuration/site-configuration) is a configuration file the `site-builder` reads during deployment to control how your site is built and served. The file itself is not uploaded to Walrus and is not accessible to visitors.

The file supports the following fields (see the [configuration reference](/docs/sites/configuration/site-configuration) for more details):
 
```json
{
  // Written automatically after the first deploy; subsequent deploys update this site
  "object_id": "0x...",
 
  // Custom HTTP response headers per resource (cache control, content type, etc.)
  "headers": {},
 
  // Client-side routing rules for SPA frameworks like React or Angular
  "routes": {
    "/app/*": "/index.html"
  },
 
  // Site metadata stored in the Sui object and displayed in wallets
  "metadata": {
    "site_name": "My Site",
    "description": "A site built on Walrus.",
    "link": "https://example.wal.app"
  },
 
  // Path patterns to exclude from deployment
  "ignore": []
}
```

> **Caution**
>
> Walrus Sites does not support dynamic redirects. Redirect plugins used in frameworks like Docusaurus resolve routes at the server level, which Walrus Sites does not have. Use the `routes` field in `ws-resources.json` to simulate client-side routing instead.
## Portals

A [portal](/docs/sites/portals/mainnet-testnet) resolves and serves a Walrus Site to a visitor's browser. When a visitor navigates to a Walrus Site URL, the portal performs the following steps:

1. Resolves the subdomain to a Sui object ID, either through a SuiNS name lookup or by decoding the Base36-encoded object ID directly from the subdomain.
2. Reads the site object's [dynamic fields](https://docs.sui.io/concepts/dynamic-fields) from Sui to get the resource map.
3. Fetches the resource from Walrus using the `blob_id` from that map.
4. Returns the resource to the browser with the appropriate headers.

This process repeats for every resource the browser requests, such as linked CSS files, scripts, and images.

The public Mainnet portal operated by Mysten Labs is available at [https://wal.app](https://wal.app). Sites with a SuiNS name are accessible directly by name. For sites without one, use the Base36-encoded object ID as the subdomain, or run `site-builder convert` to find it.

Anyone can host their own portal. For setup instructions, see [Deploy a Local Portal](/docs/sites/portals/deploy-locally).

## Local development

You can iterate on a Walrus Site entirely on your own machine by running each component locally, which avoids spending Testnet or Mainnet tokens on every change:

- **Local portal:** Run a portal on `localhost` to resolve and serve your site in the browser. See [Deploy a Local Portal](/docs/sites/portals/deploy-locally).
- **Local Walrus and Sui networks:** Run a local Walrus network, backed by a local Sui network, so the `site-builder` publishes to storage you control. See [Run a local Walrus network](/docs/system-overview/available-networks).

Point your `sites-config.yaml` and `client_config.yaml` at the local context, deploy with `site-builder`, then open the site through the local portal. Once the site works locally, switch the context back to Testnet or Mainnet to publish it. To deploy the same way on every push instead, see [Creating a GitHub Actions Workflow for Walrus Sites](/docs/sites/ci-cd/github-actions-workflow).