This guide walks you through setting up a Jenkins pipeline to build AOSP 15 using Docker on a remote SSH-connected agent.
It builds on the base Docker setup from:
Build Android 15 AOSP with Docker on Ubuntu 24.04
Why Jenkins + Docker for AOSP?
Using Jenkins to drive your AOSP builds in Docker gives you:
- Fully reproducible, isolated build environments
- Clean separation between infrastructure and build logic
- Automation-friendly setup for CI/CD integration
However, making this work in Jenkins requires a few critical adjustments. This guide explains them step-by-step.
Problems Encountered and Fixes Applied
When using the same Docker image inside Jenkins, we hit several common issues:
Problem | Fix |
---|---|
ssh-keyscan not found | Install openssh-client in the Dockerfile |
Missing repo tool | Download in the Dockerfile |
curl SSL error when downloading repo | Install ca-certificates first |
repo requires python3 | Install python3 in Dockerfile |
source fails in sh | Run sh with #!/bin/bash in the first line |
- Prepare the Docker Image
Create Dockerfile
, save this as aosp-builder/Dockerfile
:
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive
ENV LANG=C.UTF-8
ENV USE_CCACHE=1
ENV CCACHE_DIR=/ccache
ENV CCACHE_EXEC=/usr/local/bin/ccache
RUN apt-get update && apt-get install -y --no-install-recommends \
git-core \
gnupg \
flex \
bison \
build-essential \
zip \
curl \
zlib1g-dev \
libc6-dev-i386 \
x11proto-core-dev \
libx11-dev \
lib32z1-dev \
libgl1-mesa-dev \
libxml2-utils \
xsltproc \
unzip \
fontconfig \
rsync \
openssl \
openssh-client \
python3 \
ca-certificates \
&& apt-get clean
RUN curl -Lo /usr/local/bin/repo https://storage.googleapis.com/git-repo-downloads/repo && chmod +x /usr/local/bin/repo
COPY ccache /usr/local/bin/ccache
WORKDIR /workspace
Build the Image
docker build -t aosp-builder ./aosp-builder
- Set Up Jenkins Environment
Connect a Remote Agent
- A Linux host with Docker installed
- Add the user to the
docker
group - Connect it as a Jenkins SSH agent
- Label it as:
ssh-agent-with-docker
Add Gerrit Credentials, In Manage Jenkins → Credentials:
- Kind:
SSH Username with private key
- ID:
gerrit-ssh
-maksonlee - Username:
maksonlee
- Private key: Gerrit access key
- Write the Jenkinsfile
Save this as Jenkinsfile
in your repo:
pipeline {
agent { label 'ssh-agent-with-docker' }
environment {
CCACHE_PATH = "/home/administrator/.cache/ccache"
}
stages {
stage('Build AOSP') {
steps {
script {
docker.image('aosp-builder').inside("-v ${CCACHE_PATH}:/ccache ") {
sshagent(['gerrit-ssh-maksonlee']) {
sh '''#!/bin/bash
mkdir -p ~/.ssh
chmod 700 ~/.ssh
ssh-keyscan -p 29418 -H gerrit.maksonlee.com >> ~/.ssh/known_hosts
repo init -u ssh://maksonlee@gerrit.maksonlee.com:29418/platform/manifest -b android-15.0.0_r30
repo sync -c
source build/envsetup.sh
lunch aosp_arm64-trunk_staging-userdebug
m
'''
}
}
}
}
}
}
}
How Jenkins Uses -u
When Running Docker in Pipelines
When you run code inside a Docker container in Jenkins with:
docker.image('aosp-builder').inside {
sh 'id'
}
Jenkins executes the container using:
docker run -u $(id -u):$(id -g) ...
This ensures that files created inside the container are owned by the Jenkins agent user, not by root. This behavior prevents common permission issues when Jenkins accesses workspace files after the container step.
Behavior When UID:GID Doesn’t Exist in the Container
When Jenkins executes:
docker run -u 1001:1001 ...
but the container image (e.g. aosp-builder
) doesn’t define user ID 1001
or group ID 1001
in /etc/passwd
or /etc/group
, Docker still runs the process, but:
Inside the Container:
- No username is shown because there’s no entry for UID 1001.
- The process runs with raw numeric UID:GID, which may lack:
- A valid
$HOME
directory - Shell configuration (like
.bashrc
) - Access to files owned by standard users inside the container
- A valid
Consequences:
- Any shell prompt or utilities that rely on a username may fail or behave oddly.
- Your build tools (like AOSP makefiles) may fail if they assume a real user exists.
- If a tool looks for
$HOME
or tries to write to~/.cache
, it may fail if$HOME
is unset or points to a non-existent directory.