Publish with GitHub Actions
The Sitepaste deploy action publishes a directory of Markdown files from your repository and deploys your site. Push to main, and your changes are live in seconds. It is the natural fit for docs that live alongside code, and it works just as well for a blog kept in a repo.
Using the action requires an API token, which is a Pro plan feature.
Quick start
Create an API token in the Sitepaste dashboard under Account > Tokens (see authentication), and add it to your repository as a secret named SITEPASTE_TOKEN.
Then add a workflow at .github/workflows/deploy.yml:
name: Deploy docs
on:
push:
branches: [main]
paths: [docs/**]
concurrency:
group: sitepaste-deploy-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: sitepaste/integrations/actions/deploy@v1
with:
api-token: ${{ secrets.SITEPASTE_TOKEN }}
content-dir: docs
content-type: docs
Every run validates all files, publishes them in one batch, and triggers a deploy.
Inputs
| Input | Required | Default | Description |
|---|---|---|---|
| api-token | Yes | Sitepaste API token. Store it as a repository secret. | |
| content-dir | No | content | Directory containing the Markdown files. |
| content-type | No | docs | Content type for all pages: docs, blog, or standalone. |
| site-id | No | Target site. Defaults to your workspace’s default site. | |
| dry-run | No | false | Validate and preview without publishing or deploying. |
| prune | No | false | Delete pages of this content type that have no matching file, making the directory the source of truth. |
Outputs: page-count (pages published) and deploy-url (the deployed site’s URL).
Sections from directories
The action maps your directory structure to sections. Files at the root of the content directory have no section; files in a subdirectory use the subdirectory name:
docs/
overview.md → /docs/overview
guides/
getting-started.md → /docs/guides/getting-started
deployment.md → /docs/guides/deployment
With content-type: standalone, the same layout produces top-level paths instead: guides/getting-started.md is published at /guides/getting-started.
Only one directory level is used. A file nested deeper keeps the top-level directory as its section, and the action prints a warning. A section field in front matter overrides the directory; set it to an empty string to clear the section for a file inside a subdirectory.
Front matter
All fields are optional. Without front matter, the slug comes from the filename and the title from the slug.
| Field | Description |
|---|---|
| slug | URL slug for the page. |
| title | Page title. |
| section | Overrides the directory-based section. |
| description | Meta description. |
| draft | Set to true to keep the page off the deployed site. |
| tags | A YAML list of tags. |
| date or publishedAt | Publish date. Date-only values like 2026-02-19 are accepted. |
What the action does and does not do
It validates before sending. Slug format and length, title, description, and content size limits, tag limits, duplicate slugs, and dates are all checked locally first. If anything fails, the workflow fails with an annotation pointing at the exact file, and nothing is published.
It publishes everything, every run. Each run sends the full set of files as one batch. Pages are matched by slug, so existing pages are updated in place.
It does not delete unless you ask. By default, a file you remove from the repository stays published on your site. Set prune: 'true' to make the directory the source of truth: pages of the deployed content type with no matching file are deleted in the same run, so removed and renamed files clean up after themselves. Use pruning only when the repository owns that content type, since it will also delete pages of the same type that were created in the dashboard or from Obsidian. Combine it with dry-run to preview what would be deleted.
It always deploys. Every non-dry run ends by triggering a deploy, which counts against your monthly deploy quota. Use dry-run: 'true', for example on pull requests, to check changes without publishing.
Media is separate. Relative image references like  are not uploaded; the action warns about them. Upload images through the dashboard or the media API and reference them by URL.
The action source lives in the integrations repository. For the underlying API, see the pages and builds reference.