Skip to content

Deploy a Jekyll site

Publish a Jekyll site on your own domain, from your computer or from GitHub Actions. If your site is on GitHub Pages today, see Moving from GitHub Pages.

Build command bundle exec jekyll build
Output folder _site
Settings you need url, baseurl: "", and include: [_redirects, _headers] if you use them
Routing Jekyll’s default links end in .html and work as they are. Pretty permalinks need --trailing-slash; extensionless ones need --clean-urls

You need:

  • A Jekyll site that builds on your computer with bundle exec jekyll build.
  • An Evolving Edge account that can deploy workloads. See Getting Started.
  • A project in the portal, and its project ID (it starts with proj_).
  • A deploy token from the portal, under Workload → Deploy Tokens, with workload:write. Scope it to the project. See Deploy tokens.
  • The domain you want to serve the site on, for example www.example.com, and access to its DNS settings.
  • Node.js 22.12 or later, on Windows (x64), macOS, or Linux (x64 or arm64), to deploy from your computer. The deploy tool is an npm package; Jekyll itself doesn’t need Node.js, and the GitHub Action below brings its own.

Set the site’s full address, and serve it from the root of the domain:

_config.yml
url: "https://www.example.com"
baseurl: ""

jekyll-feed, jekyll-sitemap and jekyll-seo-tag use url for absolute links. Build with JEKYLL_ENV=production so they use it.

What your permalink setting looks like decides one deploy flag:

Your permalinks look like Jekyll writes Deploy with
/2026/09/29/welcome.html (the default date style) …/welcome.html Nothing extra
/about/ or permalink: pretty about/index.html --trailing-slash
/about (no slash, no .html) about.html --clean-urls

Sites often mix these: minima’s posts use the default style, while its pages set /about/. Passing both flags is fine.

Only the project that claimed a domain can deploy to it, so claim it before your first deploy. In the portal, or from the command line:

Terminal window
export EE_CDN_TOKEN=<your deploy token>
npx -p @evolving-edge/ee-cdn-cli ee-domain claim www.example.com --project proj_abc123 --wait

It prints a TXT record and a CNAME record to add at your DNS provider, then waits until the domain is active. See Custom domains for the portal steps, root domains and Cloudflare.

Terminal window
JEKYLL_ENV=production bundle exec jekyll build
npx @evolving-edge/ee-cdn-cli _site --domain www.example.com --project proj_abc123 --trailing-slash
  • ee-deploy finds _site by itself if you leave the folder out.
  • Each deploy replaces the whole site on that domain. Allow up to about 90 seconds for a new deploy to reach every node.

In your repository settings, add:

  • a secret EE_CDN_TOKEN holding your deploy token
  • variables EE_DOMAIN (for example www.example.com) and EE_CDN_PROJECT_ID

Then create .github/workflows/deploy.yml:

name: Deploy to Evolving Edge
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ee-deploy
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true
- name: Build
run: bundle exec jekyll build
env:
JEKYLL_ENV: production
- name: Deploy
uses: evolving-edge/ee-cdn-cli@v0.4.0
with:
directory: _site
domain: ${{ vars.EE_DOMAIN }}
project: ${{ vars.EE_CDN_PROJECT_ID }}
token: ${{ secrets.EE_CDN_TOKEN }}
trailing-slash: true

The Action brings its own Node.js, so the workflow needs no setup-node step. Set clean-urls: true as well if your permalinks have no extension. To keep a new release of the packaging tool from changing your deploys, pin it with builder-version; see The GitHub Action.

  • Add the workflow above. GitHub Pages can build a branch with no workflow at all; Evolving Edge deploys what your workflow builds. A Gemfile that uses the github-pages gem keeps working, and so does one that uses Jekyll 4.
  • Set baseurl: "". A project site at you.github.io/repo/ moves to the root of its own domain.
  • Extensionless links. GitHub Pages quietly serves /about from about.html. Deploy with --clean-urls to keep those links working.
  • The CNAME file in your repository does nothing here, and is harmless to keep. Your domain is set up by claiming it.
  • site.github data. Themes that read site.github.* get it from GitHub Pages automatically. In your own workflow, jekyll-github-metadata may need a token (JEKYLL_GITHUB_TOKEN) to fill it in.

Redirects. Add a _redirects file at the project root:

/old-post.html /2026/09/29/new-post.html 301
/notes/* /blog/:splat 301

Jekyll skips files whose names start with an underscore, so tell it to copy this one (and _headers, if you add it):

_config.yml
include:
- _redirects
- _headers

Rules can be 301 or 302, and * and :splat wildcards work. Redirects apply to public (Level 0) sites. Pages made by jekyll-redirect-from keep working too.

404 page. A 404.html at the site root, with permalink: /404.html, is served with a 404 status for any address that doesn’t exist. minima and most themes include one. See Custom 404 pages.

Caching. HTML pages are checked on every request, so a new deploy shows as soon as it reaches the nodes. Other files are cached for an hour. Jekyll doesn’t add hashes to asset names (minima’s assets/main.css has none), so leave caching as it is. Don’t mark assets/ as immutable, or a changed stylesheet can stay in browsers’ caches for a year.

  • Subfolder deploys. A site owns its whole domain.
  • Automatic branch builds. Deploys come from your workflow or your computer.
  • Deploy previews for each pull request. To preview, deploy to a second domain.
What you see Why What to do
/about is a 404 but /about/ works The trailing-slash redirect isn’t on Deploy with --trailing-slash
/about is a 404 and the file is about.html Extensionless permalinks Deploy with --clean-urls
Redirects in _redirects don’t work Jekyll didn’t copy the file Add it to include: in _config.yml
Styles are missing after moving from GitHub Pages baseurl still has the old /repo path Set baseurl: ""
Feed or sitemap links point at localhost:4000 Built without JEKYLL_ENV=production, or url not set Set url and build with JEKYLL_ENV=production
The deploy is refused for your domain The domain isn’t claimed for this project Claim it first
No deploy token EE_CDN_TOKEN isn’t set Export it, or add the repository secret
An authorisation error on upload The token is scoped to a project and no project was given Pass --project (or set EE_CDN_PROJECT_ID)

More in the command line troubleshooting.