---
title: "Keep remote content up to date"
description: "When content lives in a repository or a bucket, decide how and how fast edits reach your running app."
canonical_url: "https://content.comark.dev/deployment/remote-content"
---
# Keep remote content up to date

> When content lives in a repository or a bucket, decide how and how fast edits reach your running app.

Reading from a [GitHub repository](https://content.comark.dev/sources/github) or a [storage driver](https://content.comark.dev/sources/unstorage) separates content from code: writers push Markdown, and no build runs. What's left to decide is how a running instance learns that something changed. Remote sources have no file watcher, and the instance keeps parsed pages in memory once it has read them.

There are three approaches, from least to most involved. Pick the first one that meets your freshness needs.

## Let the cache expire

Give the cache a `ttl`. When a page older than the TTL is read, it's served from memory once more while the instance runs a [`refresh()`](https://content.comark.dev/reference/content/refresh) in the background: the source is re-read, added and removed files are reconciled, and the next request sees the update.

```ts [content.ts]
export const content = comarkContent({
  source: github({ repo: 'acme/docs', branch: 'main', path: 'content' }),
  cache: { ttl: 5 * 60 * 1000 }, // re-check after 5 minutes
})
```

This is the least code and it needs no webhook. The trade-off is that freshness depends on traffic: nothing refreshes until a stale page is requested, and the visitor who triggers the refresh still sees the old version. [Configure caching](https://content.comark.dev/advanced/caching) has the details on `ttl` and `swr`.

## Refresh from a webhook

For updates on push, have the repository call an endpoint that refreshes the instance:

```ts [server/api/revalidate.post.ts]
export default defineEventHandler(async () => {
  await content.refresh() // re-read the source, add and remove pages, drop stale bodies
  return { revalidated: true }
})
```

Verify the webhook's signature before acting on it. This refreshes **the process that handles the request**. With one long-running server that's the whole app; with several serverless instances, the others keep their old index until they restart or their own TTL passes. When that matters, use the next approach.

## Pin a commit and swap instances

The most robust setup treats a branch as a moving pointer and serves content from one **commit** at a time. On each request, resolve the branch's current commit through a short-lived shared cache; when it changes, drop the instance and build a new one pinned to the new commit with [`withRef()`](https://content.comark.dev/reference/content/with-ref). Every function instance converges on the same commit, listings and bodies never mix versions, and parsed content for a commit can be cached for a long time because a commit never changes.

The [GitHub source](https://content.comark.dev/sources/github#production-serve-one-commit-and-follow-the-branch) walks through this pattern step by step: resolving the pointer, namespacing the cache, swapping the instance, and warming the new commit from the webhook. It's what powers this documentation site.

## Seed cold starts

Whichever approach you pick, a cold instance still walks the repository once to build its index: one request to list files and one per file for frontmatter. On a platform that starts instances often, that's the slowest and most rate-limited part of your app.

Ship a build-time snapshot as a seed. [`withSnapshot()`](https://content.comark.dev/sources/snapshot#withsnapshot) keeps GitHub as the source of truth and lets a cold start serve from parsed data until the first refresh:

```ts [content.ts]
export const content = comarkContent({
  source: withSnapshot(
    github({ repo: 'acme/docs', branch: 'main', path: 'content' }),
    () => loadSeed('snapshot.json'),
    () => loadSeed('manifest.json')
  ),
  cache: { driver: sharedDriver() }, // a cache that outlives the instance carries updates forward
})
```

[Deploy with a content snapshot](https://content.comark.dev/deployment/with-a-snapshot) shows how to write and bundle the seed. [Warm start from a build-time seed](https://content.comark.dev/sources/github#warm-start-from-a-build-time-seed) explains how a seed and runtime edits are reconciled, and the one assumption that comparison makes.


## Sitemap

See the full [sitemap](https://content.comark.dev/sitemap.md) for all pages.
