Customize the build¶
Customize the build that runs when your website is published — add tools like Pagefind, install npm packages, or run any shell command.
Overview¶
When you publish a Silex website to a Git-based hosting (GitLab Pages, Codeberg/Forgejo Pages, SourceHut Pages), Silex generates the files that build and deploy your site on the platform's CI:
| File | Owner | Rule |
|---|---|---|
build.json |
You | Created as [{ "type": "build" }] if missing, then never touched by Silex — this is where you customize the build |
build.sh |
Silex | Regenerated from build.json at every publish — do not edit |
CI file (.gitlab-ci.yml, .forgejo/workflows/pages.yml, .build.yml) |
Silex | Immutable: it only runs sh build.sh — do not edit |
public/public.11tydata.js |
Silex | Published at every publish, configures the Eleventy build |
The build itself is always the same, for static and dynamic sites alike: build awesome (powered by Eleventy, pinned version — currently 3.1.6) builds the public/ folder into _site/, which is what gets deployed. For a plain static site, the generated public/public.11tydata.js makes this build a byte-for-byte copy — same file names, same URLs, template syntax left untouched.
To customize the build, edit build.json — not the CI file, and not build.sh.
The build.json file¶
build.json, at the root of your website's repository, is an ordered array of steps:
Two step types are available:
{ "type": "build" }— the standard Silex build (Eleventy compilespublic/into_site/). It is resolved at each publication, so it follows Silex updates (Eleventy version bumps, build improvements) without you changing anything.{ "type": "sh", "value": "<shell command>" }— any shell command. Place it before or after the build step as needed.
Silex creates build.json on the first publish if it doesn't exist, and never overwrites it afterwards. Your customizations are safe across publications.
Example: add search with Pagefind¶
Pagefind indexes your site after the build and adds client-side search:
Note the --site _site: the build output goes to the _site/ folder, so commands that post-process the built site must target _site.
Example: install npm dependencies¶
If your build needs packages from a package.json (for example Eleventy plugins), install them before the build step:
Test the build locally¶
build.sh is a plain shell script, so you can run the exact same build as the CI on your machine. Clone your website's repository, then:
The built site is in _site/. This is the fastest way to debug a failing pipeline.
CI environment variables¶
Your sh steps run inside the platform's CI job, so CI variables are available to them. On GitLab, set them in your project under Settings → CI/CD → Variables, then use them in a step:
Codeberg/Forgejo (repository Settings → Actions → Secrets) and SourceHut (build secrets) have equivalent mechanisms.
URL redirections¶
GitLab Pages supports a _redirects file at the root of the published site, using the same syntax as Netlify redirects. This is useful for:
- Preserving old URLs when you reorganize documentation or pages
- Redirecting short URLs to full paths
- Handling moved content without breaking bookmarks or search engine links
Create a _redirects file at the root of your repository:
Each line has three fields separated by spaces: the old path, the new path, and the HTTP status code (301 for permanent redirect, 302 for temporary).
The file must end up in _site/ after the build — add a copy step to your build.json:
See GitLab Pages redirects documentation for the full syntax (splat rules, query parameters, forced redirects).
Migrating from the old system (customized .gitlab-ci.yml)¶
Before build.json existed, customizing the build meant editing .gitlab-ci.yml directly and setting its first line to # silexOverwrite: false so Silex would not overwrite it.
Sites in that situation are never touched by Silex and keep working as-is — no urgency to migrate. To move such a site to the new system:
- Create
build.jsonat the root of the repository, with your custom commands as{ "type": "sh" }steps around the{ "type": "build" }step. Remember the build output is now_site/(the old scripts often ended withrm -rf public && mv _site public— drop that, and point your commands at_site). - Change the first line of your
.gitlab-ci.ymlback to# silexOverwrite: true. - Publish from Silex — Silex replaces the CI file with the standard one and generates
build.shfrom yourbuild.json.
Troubleshooting¶
The pipeline fails¶
Open the job log on your hosting platform (GitLab: CI/CD → Pipelines). Then reproduce locally with sh build.sh — you get the same build, with faster iterations.
My changes to .gitlab-ci.yml or build.sh disappear¶
Both files are generated by Silex at each publication. Put your customizations in build.json instead — that file is yours and is never overwritten.
My post-processing command has no effect¶
Check that it targets _site/ (the build output), not public/ (the build input), and that it runs after the { "type": "build" } step in build.json.
See also¶
- Build awesome configuration — customize the Eleventy build itself (plugins, filters, data files)
- Build awesome plugins — step-by-step plugin guide
- Publication transformers — hooks in the editor, before files are published
- Hosting connectors — how publication works server-side
- GitLab CI/CD docs — complete CI reference