RidgeRun Yocto Developer Guide - Yocto SState Mirror Server Automation
Yocto Sstate-Cache Builder
In this wiki you'll learn how to setup a Yocto sstate-cache mirror server using GitLab CI/CD Pipelines to build images from scratch and publish the resulting artifacts. This enables downstream projects to reuse pre-built artifacts and dramatically reduce build times.
CI/CD Platform: this project uses GitLab CI/CD Pipelines as its automation engine. All build, publish, and cleanup steps are defined in .gitlab-ci.yml and executed by GitLab Runners.
Overview
The builder performs a full Yocto build for the configured machine targets, then syncs the generated sstate-cache/ directory to a centralized mirror served over HTTPS. It also records the exact Git commit hashes of every layer used, so that clients can reproduce the same build environment.
Architecture
builder (this repo)
│
├── Full Yocto build (fetch + compile)
│
├── rsync sstate-cache/ → /mnt/fat/var/yocto/mirror/<release>/sstate-cache/
│
└── Save layer versions → /mnt/fat/var/yocto/mirror/<release>/cached_versions/
│
▼
HTTPS mirror server
(yocto.server/mirror)
│
▼
builder_client (consumer)
Prerequisites
- A GitLab runner tagged
yocto-sstate-cachewith:- Docker executor using the
dchvs/yocto:scarthgapimage (or matching your release). - A persistent volume mounted at
/mnt/fat/var/yocto/mirror/that is also served by an HTTPS web server (e.g., Nginx). - Sufficient disk space for a full Yocto build (~500 GB recommended).
- The workspace directory
/workdir/builder/owned by$USER(UID 1000, GID 1000).
- Docker executor using the
- Network access to upstream layer repositories (git.yoctoproject.org, github.com, git.openembedded.org).
How to Generate the Sstate-Cache Mirror
1. Configure the GitLab Runner
A GitLab Runner must be registered and configured on the host machine. Refer to the official GitLab Runner configuration guide for setup instructions.
2. Configure the HTTPS Server
The runner host must expose the mirror directory (/mnt/fat/var/yocto/mirror/) via HTTPS so that clients can download cached artifacts. Setting up an HTTPS server (e.g., Nginx, Apache) is outside the scope of this documentation.
3. Set CI/CD Variables
| Variable | Default | Description |
|---|---|---|
YOCTO_RELEASE |
scarthgap |
Yocto release branch to build. |
WORKSPACE |
/workdir/builder |
Working directory for the build. |
LAYERS_DIR |
/workdir/builder/layers |
Directory where layers are cloned. |
CACHED_VERSIONS_DIR |
/mnt/fat/var/yocto/mirror/scarthgap/cached_versions |
Where layer version snapshots are stored. |
SSTATE_CACHE_DIR |
/mnt/fat/var/yocto/mirror/scarthgap/sstate-cache |
Mirror destination for sstate-cache artifacts. |
SSTATE_MIRRORS_URL_BASE |
https://yocto.server/mirror |
Base URL of the mirror (used for health checks). |
SSTATE_MIRRORS_URL |
https://yocto.server/mirror/scarthgap/sstate-cache |
Full URL to the sstate-cache directory. |
4. Trigger the Pipeline
The pipeline accepts two trigger sources:
- Downstream trigger (
pipeline): Triggered by thebuilder_clientproject via thetrigger_builderstage. - Manual (
web): Run manually from the GitLab UI (CI/CD → Pipelines → Run pipeline).
5. What the Pipeline Does
- Validates the environment
- Checks that at least 300 GB of disk space is available (
check_disk_space.sh— see Appendix A.2). The threshold can be overridden via theREQUIRED_SPACE_GBvariable. - Checks that the sstate-cache server is reachable (
check_sstate_server.sh— see Appendix A.3). - Verifies workspace ownership matches the expected user/UID/GID (
verify_workspace_ownership.sh— see Appendix A.5).
- Checks that at least 300 GB of disk space is available (
- Clones all required layers at the configured
YOCTO_RELEASEbranch:poky,meta-openembedded,meta-tegra,meta-tegra-community,meta-virtualization,meta-selinux.
- Configures BitBake
- Initializes the build environment via
oe-init-build-env. - Registers all layers with
bitbake-layers add-layer.
- Initializes the build environment via
The following settings must be present in the project's build/conf/local.conf when creating the builder project:
# # Sstate-cache policy # BB_SIGNATURE_HANDLER = "OEBasicHash" BB_HASHSERVE = ""
Optionally, you can also add pressure-control settings to prevent OOM and CPU overload:
# # Modulate build pressure to avoid OOM and CPU overload # BB_PRESSURE_MAX_CPU = "300" BB_PRESSURE_MAX_IO = "200" BB_PRESSURE_MAX_MEMORY = "200"
- Builds the image
- Runs
bitbake core-image-base --runall=fetchto populate source downloads. - Runs
bitbake core-image-baseto compile everything forjetson-agx-orin-devkit.
- Runs
- Publishes the cache
- Saves a timestamped file with each layer's Git hash to
CACHED_VERSIONS_DIR(create_cached_versions_file.sh— see Appendix A.4). - Syncs the local
sstate-cache/to the mirror directory viarsync.
- Saves a timestamped file with each layer's Git hash to
- Cleans up the workspace regardless of build outcome.
6. Adding a New Machine Target
Add another bitbake invocation in the build stage:
- MACHINE=jetson-agx-orin-devkit bitbake core-image-base --runall=fetch -q - MACHINE=jetson-agx-orin-devkit bitbake core-image-base -q - MACHINE=<new-machine> bitbake core-image-base --runall=fetch -q - MACHINE=<new-machine> bitbake core-image-base -q
7. Adding a New Layer
- Add the
git clonecommand in the layers cloning section. - Add the corresponding
bitbake-layers add-layercommand. - Commit and push — the next pipeline run will include the new layer in the sstate-cache.
File Reference
| File | Purpose |
|---|---|
.gitlab-ci.yml |
Main pipeline definition. |
build/conf/local.conf |
BitBake configuration including sstate-cache policy and pressure settings. |
.gitlab-ci/check_disk_space.sh |
Pre-flight check for available disk space (default: 300 GB). |
.gitlab-ci/check_sstate_server.sh |
Validates the mirror server is reachable. |
.gitlab-ci/create_cached_versions_file.sh |
Records Git hashes of all layers after a successful build. |
.gitlab-ci/verify_workspace_ownership.sh |
Ensures workspace ownership matches expected UID/GID. |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
ERROR: the sstate-cache server ... is not reachable |
Mirror web server is down or URL is wrong. | Verify Nginx is running and SSTATE_MIRRORS_URL_BASE is correct.
|
Error: /workdir/builder is not owned by ridgerun |
Runner volume has wrong permissions. | Run chown 1000:1000 /workdir/builder on the runner host.
|
ERROR: Not enough disk space |
Workspace partition has less than 300 GB free. | Free up disk space or override with REQUIRED_SPACE_GB.
|
| Build killed by OOM | Pressure limits too high for the runner. | Lower BB_PRESSURE_MAX_* values in build/conf/local.conf.
|
| Pipeline timeout (>50h) | Build is too large or runner is underpowered. | Add more CPU/RAM or split into multiple machine builds. |
Appendix A: Source Code Reference
A.1 .gitlab-ci.yml
image: dchvs/yocto:scarthgap
stages:
- build
- clean
default:
tags:
- yocto-sstate-cache
variables:
USER: "ridgerun"
UID: "1000"
GID: "1000"
YOCTO_RELEASE: "scarthgap"
WORKSPACE: "/workdir/builder"
LAYERS_DIR: "/workdir/builder/layers"
CACHED_VERSIONS_DIR: "/mnt/fat/var/yocto/mirror/scarthgap/cached_versions"
SSTATE_CACHE_DIR: "/mnt/fat/var/yocto/mirror/scarthgap/sstate-cache"
SSTATE_MIRRORS_URL_BASE: "https://yocto_server/mirror"
SSTATE_MIRRORS_URL: "https://yocto_server/mirror/scarthgap/sstate-cache"
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "pipeline"
- if: $CI_PIPELINE_SOURCE == "web"
build:
stage: build
timeout: 50h
script:
- bash
- mkdir -p $WORKSPACE
- cd $WORKSPACE
- git clone $CI_REPOSITORY_URL -b $YOCTO_RELEASE .
- $WORKSPACE/.gitlab-ci/check_disk_space.sh
- $WORKSPACE/.gitlab-ci/check_sstate_server.sh
- $WORKSPACE/.gitlab-ci/verify_workspace_ownership.sh
- mkdir -p $LAYERS_DIR/
- cd $LAYERS_DIR/
- git clone https://git.yoctoproject.org/poky -b $YOCTO_RELEASE
- git clone https://git.openembedded.org/meta-openembedded -b $YOCTO_RELEASE
- git clone https://github.com/OE4T/meta-tegra -b $YOCTO_RELEASE
- git clone https://github.com/OE4T/meta-tegra-community -b $YOCTO_RELEASE
- git clone https://git.yoctoproject.org/meta-virtualization -b $YOCTO_RELEASE
- git clone https://git.yoctoproject.org/meta-selinux -b $YOCTO_RELEASE
- cd $WORKSPACE
- . $LAYERS_DIR/poky/oe-init-build-env $WORKSPACE/build/
- bitbake-layers add-layer $LAYERS_DIR/poky/meta
- bitbake-layers add-layer $LAYERS_DIR/poky/meta-poky
- bitbake-layers add-layer $LAYERS_DIR/poky/meta-yocto-bsp
- bitbake-layers add-layer $LAYERS_DIR/poky/meta-skeleton
- bitbake-layers add-layer $LAYERS_DIR/meta-openembedded/meta-oe
- bitbake-layers add-layer $LAYERS_DIR/meta-openembedded/meta-python
- bitbake-layers add-layer $LAYERS_DIR/meta-openembedded/meta-networking
- bitbake-layers add-layer $LAYERS_DIR/meta-openembedded/meta-gnome
- bitbake-layers add-layer $LAYERS_DIR/meta-openembedded/meta-multimedia
- bitbake-layers add-layer $LAYERS_DIR/meta-openembedded/meta-xfce
- bitbake-layers add-layer $LAYERS_DIR/meta-openembedded/meta-initramfs
- bitbake-layers add-layer $LAYERS_DIR/meta-openembedded/meta-perl
- bitbake-layers add-layer $LAYERS_DIR/meta-openembedded/meta-filesystems
- bitbake-layers add-layer $LAYERS_DIR/meta-openembedded/meta-webserver
- bitbake-layers add-layer $LAYERS_DIR/meta-virtualization
- bitbake-layers add-layer $LAYERS_DIR/meta-tegra
- bitbake-layers add-layer $LAYERS_DIR/meta-tegra-community
- MACHINE=jetson-agx-orin-devkit bitbake core-image-base --runall=fetch -q
- MACHINE=jetson-agx-orin-devkit bitbake core-image-base -q
- echo "Create the cached versions file"
- $WORKSPACE/.gitlab-ci/create_cached_versions_file.sh
- echo "Sync the sstate-cache to the mirror"
- rsync -La --info=NAME $WORKSPACE/build/sstate-cache/ $SSTATE_CACHE_DIR/
- echo "Build succeded"
cleanup_task:
stage: clean
when: always
script:
- |
if [ -n "$WORKSPACE" ]; then
rm -rf $WORKSPACE/
fi
A.2 check_disk_space.sh
#!/bin/bash
set -e
REQUIRED_SPACE_GB=${REQUIRED_SPACE_GB:-300}
AVAILABLE_GB=$(python3 -c "import shutil; print(shutil.disk_usage('$WORKSPACE').free // (1024 ** 3))")
if [ "$AVAILABLE_GB" -lt "$REQUIRED_SPACE_GB" ]; then
echo "ERROR: Not enough disk space in $WORKSPACE. Available: ${AVAILABLE_GB}G, Required: ${REQUIRED_SPACE_GB}G"
exit 1
fi
echo "INFO: Disk space OK. Available: ${AVAILABLE_GB}G (required: ${REQUIRED_SPACE_GB}G)"
A.3 check_sstate_server.sh
#!/bin/bash set -e if curl -sSf --retry 3 --retry-delay 2 -o /dev/null "$SSTATE_MIRRORS_URL_BASE"; then echo "INFO: the sstate-cache server is up" else echo "ERROR: the sstate-cache server $SSTATE_MIRRORS_URL_BASE is not reachable" exit 1 fi
A.4 create_cached_versions_file.sh
#!/bin/bash
mkdir -p $CACHED_VERSIONS_DIR/
for layer in "$LAYERS_DIR"/*/; do
name=$(basename "$layer")
hash=$(git -C "$layer" rev-parse "$YOCTO_RELEASE" 2>/dev/null) && \
echo "$name: $hash"
done > "$CACHED_VERSIONS_DIR/cached_versions_${YOCTO_RELEASE}_$(date +"%m-%d-%Y").txt"
A.5 verify_workspace_ownership.sh
#!/bin/bash OWNER_UID=$(stat -c '%u' "$WORKSPACE") OWNER_GID=$(stat -c '%g' "$WORKSPACE") OWNER_NAME=$(stat -c '%U' "$WORKSPACE") if [ "$OWNER_NAME" != "$USER" ] || [ "$OWNER_UID" != "$UID" ] || [ "$OWNER_GID" != "$GID" ]; then echo "Error: $WORKSPACE is not owned by $USER ($UID:$GID). Found $OWNER_NAME ($OWNER_UID:$OWNER_GID)" exit 1 fi