5. Creating custom Rock server images
Goal
Learn how to build custom Rock server Docker images with specific DataSHIELD packages and versions. This allows you to create reproducible, version-controlled profiles that can be shared across deployments.
Why build custom Rock images?
While the previous section showed how to add profiles using the standard obiba/rock:latest
image, building custom images provides several advantages:
- Version control: Pin specific package versions for reproducibility
- Consistency: Ensure all environments use identical package versions
Prerequisites
- Docker installed and running
- Basic understanding of Dockerfiles
- Understanding of R package dependencies
Understanding the base image
DataSHIELD provides base images that you can extend:
datashield/rock-base:6.3-R4.3
: Minimal Rock server with R 4.3obiba/rock:latest
: Standard Rock just withresourcer
DataSHIELD base images follow the pattern {dsBase-version}-R{R-version}
. In the example above, 6.3-R4.3
means dsBase version 6.3 with R version 4.3.
For custom builds, start with rock-base
for maximum control over package versions.
Example: Survival analysis profile
Here’s a complete example for creating a survival analysis Rock image:
1) Create the Dockerfile
Dockerfile.survival:
#
# Rock R Server Dockerfile with DataSHIELD Survival profile
#
# Based on: https://github.com/datashield/docker-rock
#
FROM datashield/rock-base:6.3-R4.3
# Define package versions for reproducibility
ENV DSURVIVAL_VERSION v2.3.0-dev
# Rock library path
ENV ROCK_LIB /var/lib/rock/R/library
# Install DataSHIELD packages with specific versions
# dsSurvival (survival analysis functions)
RUN Rscript -e "remotes::install_github('datashield/dsSurvival', ref = '$DSURVIVAL_VERSION', dependencies = TRUE, upgrade = FALSE, lib = '$ROCK_LIB')"
# Fix ownership (Rock runs as non-root user)
RUN chown -R rock $ROCK_LIB
2) Build the image
# Build the image with a descriptive tag
docker build -f Dockerfile.survival -t rock-survival:v2.3.0 .
# Alternative: Build with multiple tags
docker build -f Dockerfile.survival \
-t rock-survival:v2.3.0 \
-t rock-survival:latest \
.
3) Test the image locally
# Run the custom image
docker run -d --name test-survival-rock \
-p 8085:8085 \
rock-survival:v2.3.0
# Check that packages are installed
docker exec test-survival-rock Rscript -e "library(dsSurvival); packageVersion('dsSurvival')"
# Clean up
docker stop test-survival-rock
docker rm test-survival-rock
Image management and distribution
Tagging strategy
Use semantic versioning for your custom images:
# Development versions
docker build -t rock-survival:v2.3.0-dev .
# Release versions
docker build -t rock-survival:v2.3.0 .
docker build -t rock-survival:latest .
# Environment-specific tags
docker build -t rock-survival:production .
docker build -t rock-survival:staging .
Pushing to a registry
A Docker registry is a centralized repository for storing and distributing Docker images. It allows you to share images across different environments and with other developers. Docker Hub is the most popular free registry service provided by Docker, offering public repositories for open-source projects and private repositories for proprietary code.
# Tag for your registry
docker tag rock-survival:v2.3.0 your-registry.com/rock-survival:v2.3.0
# Push to registry
docker push your-registry.com/rock-survival:v2.3.0
In the case of using Docker Hub, you can use the following command to push your image to the registry:
docker tag rock-survival:v2.3.0 your-dockerhub-username/rock-survival:v2.3.0
docker push your-dockerhub-username/rock-survival:v2.3.0
Troubleshooting image builds
Common build issues
Package installation failures:
# Add error handling
RUN Rscript -e "
tryCatch({
remotes::install_github('datashield/dsBase', lib = '$ROCK_LIB')
}, error = function(e) {
cat('Error installing dsBase:', e$message, '\n')
quit(status = 1)
})
"
Permission issues:
# Ensure proper ownership at each step
RUN Rscript -e "remotes::install_github('datashield/dsBase', lib = '$ROCK_LIB')" \
&& chown -R rock $ROCK_LIB
Build context too large:
# Check build context size
du -sh .
# Use .dockerignore to exclude large files
echo "*.log" >> .dockerignore
echo "data/" >> .dockerignore
Debugging build failures
# Build with no cache to see all steps
docker build --no-cache -f Dockerfile.survival .
# Interactive debugging
docker run -it --rm datashield/rock-base:6.3-R4.3 /bin/bash
# Check intermediate layers
docker build -f Dockerfile.survival -t debug-build .
docker run -it debug-build /bin/bash
Best practices
- Version everything: Pin package versions and base image tags
- Use multi-stage builds: Keep final images small
- Layer efficiently: Order Dockerfile commands by change frequency
- Test thoroughly: Automate testing of built images
- Document well: Include package versions and build instructions
References
- DataSHIELD Docker images: docker-rock