# Move an indexed documentation site to a new hostname

> Agno Knowledge adds inspect_page_source and migrate_page_source, a guarded way to point indexed documentation at a new HTTPS host. Migration is a dry run unless you pass dry_run=False.

- Published: 2026-09-23
- Author: Ashpreet Bedi
- Categories: Changelog
- Canonical: https://www.agno.com/articles/move-an-indexed-documentation-site-to-a-new-hostname
- Markdown: https://www.agno.com/articles/move-an-indexed-documentation-site-to-a-new-hostname.md

Agno Knowledge's `inspect_page_source()` shows which `llms.txt` URL a page store namespace is bound to, and `migrate_page_source()` rebinds it. Both have async versions, `ainspect_page_source()` and `amigrate_page_source()`. Say your docs move from `docs.example.com` to `public.example.com`. The published pages, catalog rows and vectors in your page store are all fine, but the namespace stays bound to the old `llms.txt` URL, and changing that binding used to take hand-written SQL. `migrate_page_source()` now does it behind a set of guards.

![The page source relocation flow, with values from a real run. Step 1, inspect_page_source, is read only and shows the binding at docs.example.com/llms.txt, revision 1, with its storage tables. Step 2, migrate_page_source as a dry run by default, runs every guard and returns the current binding as before and after with changed=False. Step 3, migrate_page_source with dry_run=False, binds public.example.com/llms.txt at revision 2 while pages and vectors stay, and repeating it returns changed=False. Step 4, a normal sync_pages against the target, moves citations to public.example.com with 0 new embeddings, and a sync of the old URL fails.](https://www.agno.com/images/v3-0-11-page-source-relocation.png)

```python
old = "https://docs.example.com/llms.txt"
new = "https://public.example.com/llms.txt"

knowledge.inspect_page_source()  # source=old, revision=1
knowledge.migrate_page_source(expected_source=old, target_source=new)  # dry run, changed=False
knowledge.migrate_page_source(expected_source=old, target_source=new, dry_run=False)
# changed=True, after.source=new, after.revision=2

knowledge.sync_pages(url=new)  # citations now use public.example.com
```

### What `migrate_page_source()` checks

Every call, dry run or not, runs the same guards before it touches anything. Against a local PostgreSQL page store, three of them raise these errors:

```text
http:// target                     ValueError: invalid_source_url
different path on the new host     ValueError: source relocation must preserve the discovery path
current source is neither URL      ValueError: page source does not match the expected source or relocation target
```

- Both URLs must use HTTPS, and the path must match exactly. Only the host can change. The comparison is literal, so an encoded spelling of the same path fails.
- The namespace must use the catalog and vector tables that this `Knowledge` is configured with.
- The current source must equal `expected_source`, or already equal `target_source`. The second case makes a repeated call safe. It returns `changed=False` and leaves the revision alone.
- `migrate_page_source()` takes the namespace lock that sync uses. During an active sync or another relocation it raises `PageSourceBusy`. A sync that starts while a relocation holds the lock waits for it, and readers keep working throughout.

`migrate_page_source()` makes no network request, so Agno can't confirm that you own the new host or that it serves the same documentation. Check both before you apply.

### What happens after you apply

An applied relocation changes only the binding's source and bumps its revision. Pages, catalog rows and stored vectors stay as they are, so citations keep naming the old host until the next sync. The new revision also makes open `list_pages` cursors report `restart_required`.

Point every sync producer at the new URL and run a normal `sync_pages()` or `async_sync_pages()` with the same transform and `index_version`. In a real run, that sync republished the page with `public.example.com` citation URLs and made 0 embedding calls. A producer still configured with the old URL fails with "filesystem namespace is bound to another documentation source", and sync never rewrites the binding itself.

If an apply fails partway (a timeout, a lost connection), don't assume it rolled back. Call `inspect_page_source()`, or repeat the same request. Agno adds no HTTP route or model tool for relocation, so it stays an operator action.

See the [cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/05_agent_os/27_public_pages/migrate_page_source.py), and learn more about [Published Pages](https://docs.agno.com/knowledge/published-pages) in the documentation.
