Runpod Pods use custom Docker images, so you can’t directly build Docker containers or use Docker Compose on a GPU Pod. However, you can use Bazel to build and push Docker images from inside a Pod, effectively creating a “Docker in Docker” workflow.
What you’ll learn
In this tutorial, you’ll learn how to:
- Deploy a Pod for building Docker images.
- Install Bazel and Docker dependencies.
- Configure a Bazel project with rules_oci.
- Build and push a Docker image to Docker Hub.
Requirements
Before starting, you’ll need:
Step 1: Deploy a Pod
- Navigate to Pods and select + Deploy.
- Choose GPU or CPU based on your needs.
- Select an instance type (for example, A40).
- (optional) Attach a network volume for larger image builds.
- Select a template (for example, Runpod Pytorch).
- Select Deploy On-Demand.
Wait for the Pod to start, then connect via the web terminal:
- Select Connect.
- Select Start Web Terminal, then Connect to Web Terminal.
Step 2: Install dependencies
Install the required tools:
-
Update packages and install sudo:
apt update && apt install -y sudo
-
Install Docker:
curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.sh
-
Log in to Docker Hub:
-
Go to Docker Hub Security Settings and create an access token with Read/Write permissions.
-
Log in to Docker Hub, replacing
YOUR_USERNAME with your actual Docker Hub username:
docker login -u YOUR_USERNAME
When prompted for a password, paste your access token (not your Docker Hub password).
-
Install Bazel via Bazelisk:
wget https://github.com/bazelbuild/bazelisk/releases/download/v1.28.1/bazelisk-linux-amd64 && \
chmod +x bazelisk-linux-amd64 && \
sudo cp ./bazelisk-linux-amd64 /usr/local/bin/bazel
-
Verify the installations:
docker --version && bazel --version
You should see version numbers for both Docker and Bazel.
Step 3: Create the project files
Create a new directory for your Bazel project:
mkdir -p ~/docker-build && cd ~/docker-build
Create a .bazelversion file to pin to a stable Bazel version:
echo "7.4.1" > .bazelversion
Create an empty MODULE.bazel file (required by Bazel 7+):
Create the WORKSPACE file that declares your dependencies:
cat > WORKSPACE << 'EOF'
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "rules_oci",
sha256 = "56d5499025d67a6b86b2e6ebae5232c72104ae682b5a21287770bd3bf0661abf",
strip_prefix = "rules_oci-1.7.5",
url = "https://github.com/bazel-contrib/rules_oci/releases/download/v1.7.5/rules_oci-v1.7.5.tar.gz",
)
load("@rules_oci//oci:dependencies.bzl", "rules_oci_dependencies")
rules_oci_dependencies()
load("@rules_oci//oci:repositories.bzl", "LATEST_CRANE_VERSION", "oci_register_toolchains")
oci_register_toolchains(
name = "oci",
crane_version = LATEST_CRANE_VERSION,
)
load("@rules_oci//oci:pull.bzl", "oci_pull")
oci_pull(
name = "base_image",
image = "index.docker.io/library/ubuntu",
platforms = ["linux/amd64"],
tag = "22.04",
)
EOF
Create the BUILD.bazel file that defines the image build:
cat > BUILD.bazel << 'EOF'
load("@rules_oci//oci:defs.bzl", "oci_image", "oci_push")
oci_image(
name = "custom_image",
base = "@base_image",
tars = [":add_text_file"],
)
genrule(
name = "add_text_file",
srcs = [],
outs = ["text_layer.tar"],
cmd = "echo 'Hello from Bazel on Runpod!' > hello.txt && tar -cf $@ hello.txt",
)
oci_push(
name = "push_custom_image",
image = ":custom_image",
repository = "index.docker.io/USERNAME_PLACEHOLDER/custom_image",
remote_tags = ["latest"],
)
EOF
Replace YOUR_ACTUAL_USERNAME in BUILD.bazel with your actual Docker Hub username:
sed 's/USERNAME_PLACEHOLDER/YOUR_ACTUAL_USERNAME/g' BUILD.bazel > BUILD.bazel.tmp && mv BUILD.bazel.tmp BUILD.bazel
Verify the change was applied:
grep "repository" BUILD.bazel
You should see your username in the output:
repository = "index.docker.io/YOUR_ACTUAL_USERNAME/custom_image",
Step 5: Build and push the image
Run the Bazel command to build and push the Docker image:
bazel run //:push_custom_image
The first build may take several minutes as Bazel downloads dependencies and the base image. Subsequent builds will be much faster due to caching.
Once complete, your image will be available in your Docker Hub repository at your_username/custom_image:latest.
Understanding the build files
.bazelversion
This file pins the Bazel version to ensure reproducible builds. Bazelisk reads this file and automatically downloads the specified version.
WORKSPACE
This file declares your project’s external dependencies:
http_archive: Downloads the rules_oci package from GitHub.
rules_oci_dependencies: Loads transitive dependencies.
oci_register_toolchains: Registers the OCI toolchain (crane) for building images.
oci_pull: Pulls a base Docker image that your custom image builds on top of.
BUILD.bazel
This file defines what to build:
oci_image: Creates a new container image by adding layers to a base image.
genrule: Creates a tar file containing custom content to add to the image.
oci_push: Pushes the built image to a container registry.
Next steps