Gems stay on your machine; _site goes to StaticHost
Jekyll transforms templates and Markdown into a static tree under _site/. StaticHost serves _site’s contents. It does not install RubyGems, does not run bundle exec jekyll build, and does not provide GitHub Pages’ build environment. You reproduce the build locally or in CI, then upload files.
nginx uses try_files $uri $uri/ =404—no SPA index fallback. HTTPS preview is available immediately after deploy; custom domain certificates issue after DNS verifies. Pricing: ~1 day trial, then Starter $9 (1×2 GB), Pro $30 (3×10 GB + staging), Scale $65 (10×30 GB), Business $130 (30×100 GB + teams). No forever-free, cPanel, email, built-in CDN product, or PHP.
Local build
ruby -v
bundle install
bundle exec jekyll build
ls _site
Production-ish:
JEKYLL_ENV=production bundle exec jekyll build
_config.yml keys to get right:
url: "https://example.com"
baseurl: "" # or "/subdir" only if you truly host under a subpath
permalink: pretty
For a StaticHost site at the domain root, keep baseurl: "" and set url to your final HTTPS origin once known. Wrong baseurl prefixes every asset with a path that does not exist on the host.
Pack _site
cd _site && zip -r ../jekyll-site.zip .
unzip -l ../jekyll-site.zip | head
You want top-level index.html, not _site/index.html nested. See zip a static site without 404.
GitHub: publish a branch containing _site contents, or build in Actions with ruby/setup-ruby, then ship artifacts. StaticHost will not bundle install—git deploy a static site.
Worked example: fix a GitHub Pages baseurl migration
Old _config.yml for project pages:
url: "https://username.github.io"
baseurl: "/my-blog"
For StaticHost on https://blog.example.com:
url: "https://blog.example.com"
baseurl: ""
bundle exec jekyll build
grep -R "/my-blog/" _site | head # should be empty after a clean rebuild
cd _site && zip -r ../blog.zip .
Deploy, open HTTPS preview, view page source, confirm CSS hrefs start with /assets/ not /my-blog/assets/. Then DNS → verify → certificate—static hosting with a custom domain.
Plugins and GitHub Pages parity
Many tutorials assume GitHub Pages’ whitelisted plugins. On StaticHost you can use any plugin that works in *your* build environment because you run Jekyll yourself. Pin gem versions in Gemfile.lock and keep CI identical to local.
Avoid assuming .htaccess or Pages-only redirects. Prefer Jekyll redirect gems that emit HTML refresh pages if you must stay static-pure. Client-side history routers embedded in a Jekyll layout still need hash or real files—host a single-page application.
Collections and pretty permalinks
Permalinks that create directories with index.html work with nginx try_files $uri $uri/ =404. Collections (collections: in _config.yml) should emit folders you can browse in _site before upload—if you cannot ls it, visitors cannot fetch it.
When not to host Jekyll here
- You refuse to install Ruby and expected a remote build like Pages.
- You need server-side CMS editing for non-technical authors daily → different product.
- You need PHP includes or WordPress—Jekyll is not WordPress.
Migrating off GitHub Pages: build with the same Gemfile, compare _site locally, upload to StaticHost, switch DNS when preview matches. Honest free-vs-paid context: free vs paid static hosting.
Deploy flow
JEKYLL_ENV=production bundle exec jekyll build- Zip
_sitecontents; upload (or push artifacts). - Open HTTPS preview immediately.
- Spot-check posts, tags pages, and CSS from the theme.
- Roll back via deploy history if a gem update mangled output.
- DNS + certificate after verification for custom domains.
Failure table (Jekyll)
| Symptom | Likely cause | Fix |
|---|---|---|
CSS at /repo/assets 404 | Non-empty baseurl | Set baseurl: ""; rebuild |
| Posts missing | Wrong exclude / unpublished | Check front matter published and build env |
Nested _site/index.html in zip | Zipped parent | cd _site before zipping |
| Plugin missing in CI only | CI Ruby differs | Align ruby/setup-ruby with local |
| Redirect works on Pages only | Relied on Pages redirect magic | Emit static redirect pages |
CI procedure that matches your laptop
Jekyll builds diverge when Ruby patch levels and Bundler versions drift. Pin both in CI the way you pin gems.
# .github/workflows/jekyll.yml (illustrative)
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true
- run: JEKYLL_ENV=production bundle exec jekyll build
- run: cd _site && zip -r ../site.zip .
# upload site.zip to StaticHost via your chosen integration or artifact handoff
After the workflow finishes, treat the zip exactly as a local _site pack: archive root must expose index.html. If CI zips from the repository root by mistake, preview 404s the same way a laptop zip mistake does. StaticHost still will not run bundle install—CI is *your* build machine in the cloud, not a hidden host feature.
Compare theme CSS and a dated post URL on HTTPS preview before you touch DNS. Certificates for custom domains arrive only after DNS verifies; preview TLS is already there for QA.
Permalink and asset checklist before cutover
Walk this list against the production-ish build, not against jekyll serve drafts:
urlin_config.ymlmatches the eventualhttps://origin (or you accept updating it on a second deploy).baseurlis""for domain-root hosting on StaticHost.permalink: pretty(or your chosen style) produces directories you canlsunder_site.- Theme gem assets land under predictable
/assets/paths with no leftover project-page prefixes. - Collection documents each emit a file; empty collections are fine, missing templates are not.
- Feed/XML and sitemap paths return 200 on preview if you advertise them.
- No reliance on GitHub Pages redirect YAML that never became HTML files.
If any item fails, rebuild and redeploy. Do not “fix it in nginx”—StaticHost will not apply custom rewrite theater. Plans remain Starter $9 (1×2 GB), Pro $30 (3×10 GB + staging), Scale $65 (10×30 GB), Business $130 (30×100 GB + teams) after a ~1 day trial—no forever-free, email, cPanel, or built-in CDN product.
Staging on Pro is useful when a gem bump changes markup: bake on staging, click through posts, then promote the same commit’s artifact to production.
Drafts, future posts, and what preview will show
Jekyll’s published: false and future-dated posts behave according to your build flags. A local jekyll serve with drafts enabled can look richer than JEKYLL_ENV=production bundle exec jekyll build. Always QA the same artifact you will upload. If a post is missing on StaticHost, it is usually missing from _site, not “filtered by nginx.” Strict try_files $uri $uri/ =404 only serves files that exist in the zip.
FAQ
Can I use github-pages gem against StaticHost?
You can build with that gem locally for parity, then upload _site. StaticHost does not invoke it.
Where does jekyll serve fit?
Local development only.
Do I upload Gemfile?
Not required for hosting. Keep it in source control for reproducible builds; the public site needs HTML/CSS/JS/images from _site.
Why are my CSS paths /repo/assets/...?
Non-empty baseurl leftover from project-page style configs. Set baseurl: "" for root hosting and rebuild.
Can Jekyll run WordPress?
No. Different stack. WordPress needs a PHP host. For docs-style Jekyll sites also see static documentation hosting.