Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
39d0b03
Add Dockerfile and GitHub Actions workflow for building AOC Docker im…
marc-hanheide Feb 16, 2026
6400cd7
Add GitHub Actions workflow for building AOC Docker images with multi…
marc-hanheide Feb 16, 2026
b3e9a40
Update Docker build workflow to enable sequential builds and dynamic …
marc-hanheide Feb 16, 2026
7f2808b
Add CUDA Dockerfile and update build workflow for CUDA support
marc-hanheide Feb 16, 2026
6e0c408
Refactor Docker build configuration to use ROS_DISTRO variable for ta…
marc-hanheide Feb 16, 2026
a1fe98d
Add ROS installation and setup commands to CUDA Dockerfile
marc-hanheide Feb 16, 2026
a16011a
Add ROS_DISTRO argument and environment variables to CUDA Dockerfile
marc-hanheide Feb 16, 2026
71f257b
Add ARG declarations for BASE_IMAGE and ROS_DISTRO in Dockerfiles
marc-hanheide Feb 16, 2026
3f967cb
Add CUDA Dockerfile stages for base and desktop images with ROS support
marc-hanheide Feb 16, 2026
14e9331
Comment out cache-from and cache-to lines in Docker build configuration
marc-hanheide Feb 16, 2026
728dad1
Move python3-rosdep installation to the correct stage in Dockerfile
marc-hanheide Feb 16, 2026
3749deb
Add target specification for Docker build stage in workflow
marc-hanheide Feb 16, 2026
0d530ee
Add additional CUDA Dockerfile stages for base and desktop images wit…
marc-hanheide Feb 16, 2026
c55cbf2
Uncomment cache-from and cache-to lines in Docker build configuration
marc-hanheide Feb 16, 2026
ac535ef
Initialize rosdep before updating in Dockerfile
marc-hanheide Feb 16, 2026
1a5d2bf
Add support for ROS Jazzy in Docker build configuration
marc-hanheide Feb 16, 2026
37b628f
Refactor Docker build configuration by removing unused desktop stage …
marc-hanheide Feb 16, 2026
2e8ba15
Add reusable Docker image build workflow and CUDA desktop Dockerfile
marc-hanheide Feb 16, 2026
6a10ecd
Add outputs section for image digest in Docker build workflow
marc-hanheide Feb 16, 2026
8d66679
Reorder Docker build steps to ensure proper setup of buildx before bu…
marc-hanheide Feb 16, 2026
0591fb5
Fix Dockerfile syntax for ros installation
marc-hanheide Feb 16, 2026
7665cde
feat: add seperate display container
cooperj Feb 16, 2026
e9e2e9a
Merge branch 'main' into display
marc-hanheide Feb 16, 2026
940098f
ci: rename the container from `display` to `vnc` add CI building
cooperj Feb 17, 2026
2f6514f
ci: disable requirement for ros distro, allowing for non ros builds
cooperj Feb 17, 2026
21c932c
Merge branch 'main' into display
cooperj Feb 25, 2026
faa9021
fix: install turbovnc this time and fix ci
cooperj Feb 25, 2026
be348cd
feat: cleaned up the vnc container, added docker cli
cooperj Feb 25, 2026
cafb62e
chore: run linter
cooperj Feb 25, 2026
2b3873c
fix: restore wallpaper functionality
cooperj Mar 2, 2026
3afa5ca
chore: add back the old wallpaper as an option
cooperj Mar 2, 2026
9de3968
chore: update readme to force build
cooperj Mar 10, 2026
bc94c5c
ci: break the vnc + devtools into two separate containers
cooperj Mar 13, 2026
dba3a96
fix: rm the distro line
cooperj Mar 13, 2026
15994e0
Merge branch 'main' into display
cooperj Mar 13, 2026
f4049ed
fix: change context to inside docker folder matching ci actions
cooperj Mar 13, 2026
b788647
Apply suggestions from code review
cooperj Mar 13, 2026
a7b6e0e
Remove unused VNC_PORT env var from vnc.dockerfile (#7)
Copilot Mar 13, 2026
0eeb0a3
fix: rm the warning on contexts outside of a terminal inside vnc term…
cooperj Mar 20, 2026
107fd67
feat: add GPU support for VNC and ROS containers in Docker Compose
marc-hanheide Mar 23, 2026
d760267
docs: Revise README for repository name and container details
cooperj Mar 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions .github/workflows/_build-image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ on:
description: Image name on the LCAS registry
required: true
type: string
ros_distro:
required: true
type: string
dockerfile:
required: true
type: string
Expand Down Expand Up @@ -55,16 +52,12 @@ jobs:
with:
flavor: latest=false
labels: |
org.opencontainers.image.description=AOC Docker Image (${{ inputs.push_image }}, ${{ inputs.ros_distro }})
org.opencontainers.image.description=AOC Docker Image (${{ inputs.push_image }})
org.opencontainers.image.authors=L-CAS Team
images: lcas.lincoln.ac.uk/${{ inputs.push_image }}
tags: |
type=raw,value=${{ inputs.ros_distro }}-staging
type=raw,enable=${{ github.event_name != 'pull_request' }},value=${{ inputs.ros_distro }}-latest
type=ref,enable=${{ github.event_name != 'pull_request' }},event=branch,prefix=${{ inputs.ros_distro }}-
type=semver,pattern={{version}},prefix=${{ inputs.ros_distro }}-
type=semver,pattern={{major}}.{{minor}},prefix=${{ inputs.ros_distro }}-
type=semver,pattern={{major}},prefix=${{ inputs.ros_distro }}-
type=raw,value=staging
type=raw,enable=${{ github.event_name != 'pull_request' }},value=latest

- uses: docker/setup-buildx-action@v3

Expand All @@ -76,10 +69,9 @@ jobs:
file: ./${{ inputs.dockerfile }}
platforms: ${{ inputs.architectures }}
push: true
cache-from: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:${{ inputs.ros_distro }}
cache-to: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:${{ inputs.ros_distro }},mode=max
cache-from: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:latest
cache-to: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:latest,mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
BASE_IMAGE=${{ inputs.base_image }}
ROS_DISTRO=${{ inputs.ros_distro }}
82 changes: 82 additions & 0 deletions .github/workflows/_build-ros-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Build a single ROS Docker image (reusable)

on:
workflow_call:
inputs:
base_image:
description: The FROM base image
required: true
type: string
push_image:
description: Image name on the LCAS registry
required: true
type: string
ros_distro:
required: true
type: string
dockerfile:
required: true
type: string
architectures:
required: true
type: string
outputs:
digest:
description: Image digest from the build
value: ${{ jobs.build.outputs.digest }}
secrets:
LCAS_REGISTRY_PUSHER:
required: true
LCAS_REGISTRY_TOKEN:
required: true

jobs:
build:
runs-on: [lcas, qemu]
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/setup-node@v4
- uses: actions/checkout@v3
- run: echo "BRANCH=${GITHUB_REF##*/}" >> $GITHUB_ENV

- uses: docker/login-action@v3
with:
registry: lcas.lincoln.ac.uk
username: ${{ secrets.LCAS_REGISTRY_PUSHER }}
password: ${{ secrets.LCAS_REGISTRY_TOKEN }}

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
flavor: latest=false
labels: |
org.opencontainers.image.description=AOC Docker Image (${{ inputs.push_image }}, ${{ inputs.ros_distro }})
org.opencontainers.image.authors=L-CAS Team
images: lcas.lincoln.ac.uk/${{ inputs.push_image }}
tags: |
type=raw,value=${{ inputs.ros_distro }}-staging
type=raw,enable=${{ github.event_name != 'pull_request' }},value=${{ inputs.ros_distro }}-latest
type=ref,enable=${{ github.event_name != 'pull_request' }},event=branch,prefix=${{ inputs.ros_distro }}-
type=semver,pattern={{version}},prefix=${{ inputs.ros_distro }}-
type=semver,pattern={{major}}.{{minor}},prefix=${{ inputs.ros_distro }}-
type=semver,pattern={{major}},prefix=${{ inputs.ros_distro }}-

- uses: docker/setup-buildx-action@v3

- name: Build and push
id: build
uses: docker/build-push-action@v6
with:
context: ./docker
file: ./${{ inputs.dockerfile }}
platforms: ${{ inputs.architectures }}
push: true
cache-from: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:${{ inputs.ros_distro }}
cache-to: type=registry,ref=lcas.lincoln.ac.uk/cache/${{ inputs.push_image }}:${{ inputs.ros_distro }},mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
BASE_IMAGE=${{ inputs.base_image }}
ROS_DISTRO=${{ inputs.ros_distro }}
32 changes: 24 additions & 8 deletions .github/workflows/docker-build-and-push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ on:
workflow_dispatch:

jobs:

# ── Level 0: no dependencies ──────────────────────────────────────────────
vnc:
uses: ./.github/workflows/_build-image.yaml
with:
base_image: debian:trixie-slim
push_image: vnc
dockerfile: vnc.dockerfile
architectures: linux/amd64,linux/arm64
secrets: inherit

ros-humble:
uses: ./.github/workflows/_build-image.yaml
uses: ./.github/workflows/_build-ros-image.yaml
with:
base_image: ros:humble
push_image: ros
Expand All @@ -23,7 +30,7 @@ jobs:
secrets: inherit

ros-jazzy:
uses: ./.github/workflows/_build-image.yaml
uses: ./.github/workflows/_build-ros-image.yaml
with:
base_image: ros:jazzy
push_image: ros
Expand All @@ -33,7 +40,7 @@ jobs:
secrets: inherit

ros-cuda-humble:
uses: ./.github/workflows/_build-image.yaml
uses: ./.github/workflows/_build-ros-image.yaml
with:
base_image: nvidia/cuda:11.8.0-runtime-ubuntu22.04
push_image: ros_cuda
Expand All @@ -43,7 +50,7 @@ jobs:
secrets: inherit

ros-cuda-jazzy:
uses: ./.github/workflows/_build-image.yaml
uses: ./.github/workflows/_build-ros-image.yaml
with:
base_image: nvidia/cuda:12.9.1-runtime-ubuntu24.04
push_image: ros_cuda
Expand All @@ -53,10 +60,19 @@ jobs:
secrets: inherit

# ── Level 1: base_image is the exact digest from level 0 ─────────────────
vnc-devtools:
needs: vnc
uses: ./.github/workflows/_build-image.yaml
with:
base_image: lcas.lincoln.ac.uk/vnc@${{ needs.vnc.outputs.digest }}
push_image: vnc_devtools
dockerfile: vnc_devtools.dockerfile
architectures: linux/amd64,linux/arm64
secrets: inherit

ros-cuda-desktop-humble:
needs: ros-cuda-humble
uses: ./.github/workflows/_build-image.yaml
uses: ./.github/workflows/_build-ros-image.yaml
with:
# Digest is immutable — no staging tag, no race condition, no cleanup
base_image: lcas.lincoln.ac.uk/ros_cuda@${{ needs.ros-cuda-humble.outputs.digest }}
Expand All @@ -68,11 +84,11 @@ jobs:

ros-cuda-desktop-jazzy:
needs: ros-cuda-jazzy
uses: ./.github/workflows/_build-image.yaml
uses: ./.github/workflows/_build-ros-image.yaml
with:
base_image: lcas.lincoln.ac.uk/ros_cuda@${{ needs.ros-cuda-jazzy.outputs.digest }}
push_image: ros_cuda_desktop
ros_distro: jazzy
dockerfile: cuda_desktop.dockerfile
architectures: linux/amd64
secrets: inherit
secrets: inherit
72 changes: 65 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,67 @@
# aoc_container_base
# LCAS/aoc_container_base

A repository of verstile ROS-enabled Docker containers, orginally developed as apart of the [Agri-OpenCore (AOC) project](https://agri-opencore.org).
A repository of verstile Docker containers, orginally developed as apart of the [Agri-OpenCore (AOC) project](https://agri-opencore.org) stack.

| Container Name | Tags | Purpose |
| ------------------------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `lcas.lincoln.ac.uk/ros` | { `humble`, `jazzy` } | Base ROS Container, the minimal environment you need for ROS |
| `lcas.lincoln.ac.uk/ros_cuda` | { `humble`, `jazzy` } | ROS + Nvidia. When you need to use a GPU in your ROS environment for either better quality simulation or AI workloads. |
| `lcas.lincoln.ac.uk/ros_cuda_desktop` | { `humble`, `jazzy` } | ROS + Nvidia + Packages. Installs the `ros-{distro}-desktop` varient so there is the full ROS stack available. |
This repository provides us with the following containers:

| Container Name | Varients | Purpose | File |
| --- | --- | --- | --- |
| `lcas.lincoln.ac.uk/ros` | `humble` `jazzy` | Base ROS Container, the minimal environment you need for ROS | [base.dockerfile](base.dockerfile) |
| `lcas.lincoln.ac.uk/ros_cuda` | `humble` `jazzy` | ROS + Nvidia. When you need to use a GPU in your ROS environment for either better quality simulation or AI workloads. | [cuda.dockerfile](cuda.dockerfile) |
| `lcas.lincoln.ac.uk/ros_cuda_desktop` | `humble` `jazzy` | ROS + Nvidia + Packages. Installs the `ros-{distro}-desktop` varient so there is the full ROS stack available. | [cuda_desktop.dockerfile](cuda_desktop.dockerfile) |
| `lcas.lincoln.ac.uk/vnc` | `latest` | Standalone VNC container that can take X11 visualisations and show them in a browser. | [vnc.dockerfile](vnc.dockerfile) |
| `lcas.lincoln.ac.uk/vnc_devtools` | `latest` | Adds development tools such as a terminal for the standard VNC environment. | [vnc_devtools.dockerfile](vnc_devtools.dockerfile) |

These containers are built from three standard container images, `ros`, `nvidia/cuda` and `debian`. Each container is either built from one of these pre-existing images or one derrived from it in this pattern.

```mermaid
flowchart TB
library/ros["library/ros"] --> lcas/ros["lcas/ros"]
nvidia/cuda["nvidia/cuda"] --> lcas/ros_cuda["lcas/ros_cuda"]
lcas/ros_cuda --> lcas/ros_cuda_desktop["lcas/ros_cuda_desktop"]
debian["debian"] --> vnc["vnc"]
vnc --> vnc_devtools["vnc_devtools"]

style library/ros stroke-width:1px,stroke-dasharray: 1,fill:#ffffff
style lcas/ros fill:#BBDEFB
style nvidia/cuda stroke-width:1px,stroke-dasharray: 1,fill:#ffffff
style lcas/ros_cuda fill:#BBDEFB
style lcas/ros_cuda_desktop fill:#BBDEFB
style debian stroke-width:1px,stroke-dasharray: 1,fill:#ffffff
style vnc fill:#BBDEFB
style vnc_devtools fill:#BBDEFB
```

## How can I use this?

This works best if you follow the [`ros2_workspace_template`](https://github.com/lcas/ros2_pkg_template), use this as a template to build your own repositories, that contain the packages you want to ship.

You can work either inside the devcontainer or by running the container yourself, and then when you are ready start by enabling the deployment workflows and adding automated testing. Then once you're happy move this onto a real robot platform - and keep iterating till it works!

```mermaid
%%{init: {"theme":"neutral","flowchart":{"curve":"linear"}}}%%
flowchart LR
Dev["Develop in the Devcontainer"]
Workflows["Activate Workflows to Build and Push Images"]
AutoTest["Add Automated Testing Steps Where Possible"]
Deploy["Add/Update Your Container on to the Platform Deployment Repository"]
RealTest["Test the Packages on a Real Robot"]
Outcome{"Did it work well?"}
Fix(("Make changes that will fix the issues"))
Complete(["✅ Complete"])

%% main flow
Dev --> Workflows --> AutoTest --> Deploy --> RealTest --> Outcome
Outcome -- "Yes" --> Complete
Outcome -- "No" --> Fix --> AutoTest

%% iterative dev loop (renders as a real loop in GitHub)
Dev -. "Iterate" .-> Iter(("🔄"))
Iter -.-> Dev

%% keep it subtle (GitHub-friendly)
classDef loop fill:transparent,stroke:#999,stroke-width:1px;
class Iter loop;
```

#
32 changes: 15 additions & 17 deletions base.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ ENV ROS_DISTRO=${ROS_DISTRO}
ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update \
&& apt-get upgrade -y && \
apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
gnupg \
cmake \
git \
curl \
wget \
unzip \
ros-${ROS_DISTRO}-ros-base \
ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \
python3-colcon-common-extensions && \
rm -rf /var/lib/apt/lists/*
&& apt-get upgrade -y \
&& apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
gnupg \
cmake \
git \
curl \
wget \
unzip \
ros-${ROS_DISTRO}-ros-base \
ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \
python3-colcon-common-extensions \
&& rm -rf /var/lib/apt/lists/*

ENV LANG=en_US.UTF-8

Expand All @@ -39,8 +39,7 @@ RUN groupadd --gid $USER_GID $USERNAME \
&& useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \
&& apt-get update \
&& apt-get install -y --no-install-recommends sudo \
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\
&& chmod 0440 /etc/sudoers.d/$USERNAME \
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL >/etc/sudoers.d/$USERNAME && chmod 0440 /etc/sudoers.d/$USERNAME \
&& rm -rf /var/lib/apt/lists/*

# Cyclone DDS Config
Expand All @@ -60,4 +59,3 @@ ENV CYCLONEDDS_URI=file:///etc/cyclonedds.xml

USER ${USERNAME}
CMD ["bash", "-l"]

24 changes: 24 additions & 0 deletions compose.gpu.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# GPU override — merge on top of compose.yml when an NVIDIA GPU is available.
# Requires the NVIDIA Container Toolkit to be installed and configured.
#
# Usage: docker compose -f compose.yml -f compose.gpu.yml up

services:

vnc:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu, utility, video, graphics]

ros:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu, compute, utility, video, graphics]
Loading