# Deploying
You can host your built Framework app on any static site hosting service, or self-host it with any static site server. This guide covers automating deploys to [GitHub Pages](https://pages.github.com/) with [GitHub Actions](https://github.com/features/actions), and assumes that you are hosting your Framework project’s git repository on GitHub.
If you don’t yet have an app ready to deploy, create one by following our [Getting started guide](./getting-started).
In your git repository, create the file `.github/workflows/deploy.yml` with the following contents:
```yaml
name: Deploy
on:
# Run this workflow whenever a new commit is pushed to main.
push: {branches: [main]}
# Run this workflow once per day, at 10:15 UTC
schedule: [{cron: "15 10 * * *"}]
# Run this workflow when triggered manually in GitHub’s UI.
workflow_dispatch: {}
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- uses: actions/configure-pages@v4
- uses: actions/upload-pages-artifact@v3
with:
path: dist
- name: Deploy
id: deployment
uses: actions/deploy-pages@v4
```
Commit this file to your main branch and push to GitHub. Then follow [GitHub’s instructions](https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow) to specify GitHub Actions as the source for GitHub Pages.
After you’ve performed these steps, the `deploy.yml` above will automatically build and deploy your app once per day (to keep your data up-to-date), as well as whenever you push new commits to your GitHub repository’s main branch (so you can make changes at any time). You will also be able to manually trigger a deploy through GitHub’s user interface. If you like, you can disable any of these triggers (`push`, `schedule`, or `workflow_dispatch`) by updating the `deploy.yml` file.
### Caching
If some of your data loaders take a long time to run, or simply don’t need to be updated every time you build, you can set up caching to automatically re-use existing data from previous builds. To do that, add the following steps to your `deploy.yml` before `npm run build`:
```yaml
jobs:
deploy:
steps:
# …
- id: date
run: echo "date=$(TZ=America/Los_Angeles date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- id: cache-data
uses: actions/cache@v4
with:
path: src/.observablehq/cache
key: data-${{ hashFiles('src/data/*') }}-${{ steps.date.outputs.date }}
# …
```
This will only cache data loaders that live under src/data. You’ll need to edit the paths above if you’ve configured a source root other than src, or if the data loaders you want to cache live elsewhere.
This uses one cache per calendar day (in the `America/Los_Angeles` time zone). If you deploy multiple times in a day, the results of your data loaders will be reused on the second and subsequent runs. You can customize the `date` and `cache-data` steps to change the cadence of the caching. For example you could use `date +'%Y-%U'` to cache data for a week or `date +'%Y-%m-%dT%H'` to cache it for only an hour.