Lightweight Alpine-based container for building and serving Jekyll static sites with live reload support.
Every build ships a Sigstore-signed SBOM and a full Trivy scan — verify them yourself, no login required:
gh attestation verify oci://ghcr.io/oorabona/jekyll:latest --owner oorabona
Full walkthrough (SBOM payload, Trivy findings, multi-arch manifest inspection, upstream dependency tracking) → https://oorabona.github.io/docker-containers/verify-images/
# Serve a site with live reload
docker run --rm -it \
-v "$(pwd):/site" \
-p 4000:4000 \
-p 35729:35729 \
ghcr.io/oorabona/jekyll:latest
# Build only (no server)
docker run --rm \
-v "$(pwd):/site" \
ghcr.io/oorabona/jekyll:latest \
build
# Build to custom destination
docker run --rm \
-v "$(pwd):/site" \
ghcr.io/oorabona/jekyll:latest \
build --destination /site/public
services:
jekyll:
image: ghcr.io/oorabona/jekyll:latest
ports:
- "4000:4000"
- "35729:35729"
volumes:
- ./site:/site
command: ["serve", "--host", "0.0.0.0", "--livereload", "--force_polling"]
Then run:
docker compose up
Site available at: http://localhost:4000
The Compose profile runs with uid/gid 1000 by default, matching the image's
jekyll user. To keep bind-mounted files owned by your host account, pass your
numeric identity explicitly:
LOCAL_UID="$(id -u)" LOCAL_GID="$(id -g)" docker compose up
With rootless Podman, container uids are remapped, so uid 1000 in the
container is not your host account and cannot write the bind mount
(Permission denied @ dir_s_mkdir - /site/.jekyll-cache). Map your host
identity into the container instead:
podman run --rm --userns=keep-id -v "$(pwd):/site" ghcr.io/oorabona/jekyll:latest build
jekyll-feed - RSS/Atom feed generationjekyll-seo-tag - SEO optimization meta tagsjekyll-sitemap - XML sitemap generationAll dependency versions are pinned for reproducible builds:
| Argument | Default | Description |
|---|---|---|
RUBY_VERSION | 3.3 | Ruby major version |
ALPINE_VERSION | 3.21 | Alpine Linux version |
JEKYLL_VERSION | 4.4.1 | Jekyll core version |
BUNDLER_VERSION | 4.0.6 | Bundler dependency manager |
WEBRICK_VERSION | 1.9.2 | WebRick HTTP server |
JEKYLL_FEED_VERSION | 0.17.0 | RSS/Atom feed plugin |
JEKYLL_SEO_TAG_VERSION | 2.8.0 | SEO meta tags plugin |
JEKYLL_SITEMAP_VERSION | 1.4.0 | Sitemap generation plugin |
| Port | Purpose |
|---|---|
4000 | Jekyll development server |
35729 | LiveReload websocket |
Mount your Jekyll site directory to /site:
-v "$(pwd):/site"
your-site/
├── _config.yml # Jekyll configuration
├── _posts/ # Blog posts
├── _layouts/ # HTML templates
├── _includes/ # Reusable components
├── assets/ # CSS, JS, images
├── Gemfile # Additional gem dependencies
└── _site/ # Generated output (auto-created)
The container does not run bundle install. For gems the image does not ship,
see Adding Custom Dependencies.
The image runs as the non-root jekyll user (uid/gid 1000), and /site is
writable by that user. Its HOME and Ruby/Bundler cache locations use /tmp,
which is writable even when Docker runs the image with an arbitrary numeric uid
that has no passwd entry. For a bind mount, use your host uid/gid so generated
files remain owned by your account:
# Run as the host user
docker run --user "$(id -u):$(id -g)" \
-v "$(pwd):/site" \
ghcr.io/oorabona/jekyll:latest build
For serving Jekyll sites in production:
Build static files locally:
docker run --rm -v "$(pwd):/site" ghcr.io/oorabona/jekyll:latest build
Serve with a dedicated web server:
Use nginx, Apache, or a CDN to serve the _site/ directory. The Jekyll container is intended for development only.
127.0.0.1 for local-only access:
-p 127.0.0.1:4000:4000
4000 to the public internet_config.yml files to prevent sensitive data leaksAll Ruby gem versions are pinned and monitored for updates:
| Gem | Version | Purpose |
|---|---|---|
| bundler | 4.0.6 | Dependency management |
| webrick | 1.9.2 | HTTP server |
| jekyll-feed | 0.17.0 | RSS/Atom feed generation |
| jekyll-seo-tag | 2.8.0 | SEO meta tags |
| jekyll-sitemap | 1.4.0 | XML sitemap generation |
The image installs its gems at build time and runs as a non-root user, so it
does not install a site's Gemfile. To add gems, extend the image:
FROM ghcr.io/oorabona/jekyll:latest
USER root
RUN gem install jekyll-theme-cayman jekyll-redirect-from jemoji
USER jekyll
jekyll/
├── Dockerfile # Alpine-based build
├── config.yaml # Dependency versions
├── version.sh # Upstream version checker
├── docker-compose.yml # Local development setup
└── README.md # This file
The Dockerfile uses a single-stage build:
ruby:{RUBY_VERSION}-alpine{ALPINE_VERSION}/siteserve --host 0.0.0.0 --livereload# Build with default versions
./make build jekyll
# Build with specific Jekyll version
./make build jekyll 4.4.1
# Build with custom arguments
docker build \
--build-arg JEKYLL_VERSION=4.4.0 \
--build-arg RUBY_VERSION=3.2 \
-t jekyll:custom .
# Check current version
cd jekyll && ./version.sh
# Check latest upstream version
cd jekyll && ./version.sh latest
# JSON output for automation
cd jekyll && ./version.sh --json
Content type
Image
Digest
sha256:617d0281c…
Size
77.6 MB
Last updated
about 4 hours ago
docker pull oorabona/jekyll