I needed a small documentation site for a workflow I know well: building and
debugging subgraphs on The Graph. I used Thally, an
open-source docs engine with a managed host, and the whole thing took a bit
under two hours including the detours. This is the path I would take if I
had to do it again, with the detours called out so you can skip them.
The result is live at
yashpersonal.thally.app and the source is
at github.com/yash251/yashpersonaldocs.
What Thally is, in one paragraph
Thally is a Next.js docs framework. Pages are MDX files in src/content/,
navigation is one JSON file, and branding is one TypeScript file. The same
source renders HTML for people and Markdown, llms.txt, and a read-only MCP
endpoint for AI tools, without any extra configuration. Thally Cloud hosts
the site, builds on every push to main, and gives you a *.thally.app
address. The free tier allows one managed site and needs no card.
Prerequisites
- A GitHub account. Thally creates the docs repository for you and needs to install a GitHub App on your account.
- Node.js 20 or newer, for running checks locally. You can skip this and edit on GitHub, but you will want the local preview.
- Something to document. Pick one workflow you can explain end to end. I chose "deploy a subgraph and debug it when it fails" because I have answered that question hundreds of times in support.
Step 1: Create the site in Thally Cloud
- Sign in at app.thally.io with GitHub. The first sign-in starts a 7-day trial of the paid tier; it drops to the free tier afterwards and your site stays up.
- Select Create a site. Enter a name, an optional description, and
the
thally.appsubdomain you want. Thally checks availability inline. - On Where do your docs live?, choose Create a new docs repository.
Detour 1: don't use "Import from a docs URL". The wizard offers to
import an existing public docs site by URL. I pointed it at
thegraph.com/docs/en/ out of curiosity. It reported "0 pages imported;
14 pages failed" and offered no per-page reason beyond "contact support".
That site is a client-rendered Next.js app behind Cloudflare, so a crawler
gets empty shells. Importing someone else's docs was never the right move
for this exercise anyway, but the error message would not have helped me
if it had been.
- Install the Thally Labs GitHub App when prompted. It asks for All repositories with read and write access to administration, code, pull requests, and workflows.
Detour 2: that scope is broader than you want. Thally needs
administration access to create the repo, and GitHub only lets an App reach
a repo it creates if it was installed on all repositories. Install it, and
then once the site is live, go to GitHub Settings > Applications > Thally
Labs > Configure and switch to Only select repositories with just the
docs repo. Thally keeps working because that is the only repo it touches.
- Skip Product sources and Knowledge surfaces. Both are for Thally's change-tracking feature and are not needed for a first site.
- On Ready to publish, check the repo name. Mine was
yash251/yashpersonaldocs. Select Publish site.
Detour 3: the repo is created private, and you cannot change that
here. The review screen shows "New repository from the Thally template ·
private" with an Edit button, but Edit restarts the repository step
instead of offering a visibility toggle. I published anyway and flipped it
afterwards:
gh repo edit yash251/yashpersonaldocs --visibility public --accept-visibility-change-consequences
The first deployment took a few minutes from pressing Publish to the
template site responding at the thally.app address.
Step 2: Clone and look around
gh repo clone yash251/yashpersonaldocs
cd yashpersonaldocs
npm install
npx thally dev
The dev server runs on port 3040 and prints the local /llms.txt and
/api/mcp endpoints. The files you will actually edit:
| File | What it controls |
|---|---|
src/content/*.mdx |
Pages. Frontmatter needs title and description. |
docs.json |
Tabs, sidebar groups, page order, navbar button. |
src/data/site.ts |
Site name, description, repo URL, header links, brand colours. |
Everything else in the repo is the Thally runtime, synced in as a pinned
snapshot. Leave it alone; thally starter update manages it.
The template ships with example pages (components, customisation, an API
reference tab wired to an openapi.yaml, three Thally guides). I deleted
all of them. A docs site for one tool does not need a tab explaining Thally.
Step 3: Decide the shape before writing
I kept it to three pages plus the changelog:
- Introduction, home layout: what a subgraph is, who the site is for, two cards pointing at the other pages.
-
Quickstart: install the Graph CLI, create a subgraph in Subgraph
Studio, scaffold with
graph init, build, deploy, and verify with a query. One concrete contract (Gravity on mainnet) so the steps are reproducible and the result is checkable. - Guide: Debug a failed subgraph: how to query the index node for status, a table mapping error messages to causes, a fix for each, and a troubleshooting accordion for the cases that look like failures but are not.
The quickstart ends by linking to the guide. The guide assumes the
quickstart. That is the whole journey.
Step 4: Write the content
One thing about writing MDX for Thally that is worth knowing up front.
The components are good and you do not need many. <Steps>/<Step>
for sequences, <Tabs>/<Tab> for npm-vs-yarn, <Note type="warning">
for the thing that will bite people, <Accordion> for detail most readers
can skip, <CardGroup> on the home page. Plain Markdown tables render well
and I used them for prompt-to-answer mappings and error-to-cause lookups.
The icon names on <Card> are Lucide names (rocket, bug, book-open).
Here is the navigation for the finished site:
{
"navbar": { "primary": { "label": "Deploy your first subgraph", "href": "/quickstart" } },
"tabs": [
{
"tab": "Get started",
"groups": [{ "group": "Get started", "pages": ["introduction", "quickstart"] }]
},
{
"tab": "Guides",
"groups": [{ "group": "Operate", "pages": ["guides/debug-a-failed-subgraph"] }]
},
{ "tab": "Changelog", "href": "/changelog" }
]
}
And the only part of src/data/site.ts that matters:
export const siteConfig: SiteConfig = {
...templateSiteConfig,
name: "Subgraph Handbook",
description: "A practical guide to building, deploying, and debugging subgraphs on The Graph.",
repoUrl: "https://github.com/yash251/yashpersonaldocs",
links: [
{ label: "Quickstart", href: "/quickstart" },
{ label: "Debug a failed subgraph", href: "/guides/debug-a-failed-subgraph" },
{ label: "GitHub", href: "https://github.com/yash251/yashpersonaldocs" },
{ label: "Changelog", href: "/changelog" },
],
};
Step 5: Check before you push
npx thally check
npm run build
thally check lints frontmatter, validates every link and every page
referenced from docs.json, and scores the site for AI readability. It
caught nothing on my content, which I take as a sign the rules are
sensible rather than that I am perfect. npm run build is the real test;
an MDX syntax error only shows up here.
While the dev server is running, curl localhost:3040/api/agent-readiness
returns a scored JSON report. Mine said 98 out of 100, with the one
deduction being a changelog description under 50 characters. I fixed it.
That report is a nice touch: it tells you exactly which page and why.
Step 6: Publish
git add -A
git commit -m "docs: replace template with Subgraph Handbook"
git push origin main
That is the whole publishing step. Thally Cloud builds on push to main,
and the dashboard's Deployments page shows the build. Confirm the live
site before you call it done; a successful push is not a successful
deploy.
Detour 4: the dashboard name wins, and renaming it does not take. I
set name: "Subgraph Handbook" in src/data/site.ts, and that is what my
local build showed. The live site showed "yashpersonal", the name I typed
into the wizard. Thally Cloud serves the dashboard's site name and
description over whatever is in the repo, and nothing in the file or the
docs says so. The comment above the block reads "Managed by Thally Cloud
onboarding", which in hindsight is the hint. I renamed the site in Site
settings, which says the change goes live on the next publish. I then
tried Redeploy from the Deployments page and a fresh push to main; both
built successfully and both still served the old name. At the time of
writing the header still says "yashpersonal". If you care about the name,
get it right in the wizard.
Detour 5: Renovate. Shortly after the repo was created, a bot opened a
"Configure Renovate" pull request. It comes with the template. It is
harmless, but if you are watching CI for your own commit it is confusing
to see a run against a branch you did not push.
What I'd tell a colleague
- Budget 15 minutes for the wizard and GitHub App, 10 minutes to delete the template content, and the rest for writing. The tool gets out of the way quickly.
- Write the quickstart against one real, verifiable example. "Replace with your contract" is not a quickstart.
- Run
npm run buildlocally before every push. The content check is fast but it is not a compile. - Narrow the GitHub App scope as soon as the site is live.
- Keep the page count low. The template invites you to fill tabs; resist.












