Run a local Opal server with a Rock R server and sample data (CNSIM) using Docker Compose, ready for DataSHIELD client testing.
Requires Docker with Compose >= 2.30 (check with docker compose version). Prefix commands with sudo on Linux if your user is not in the docker group.
git clone https://github.com/FederatedMethods/datashield_dev_install
cd datashield_dev_install/docker
docker compose up -d
The first start downloads the sample data and sets everything up, which takes a few minutes. Follow progress with docker compose logs -f opal.
| What | Where / credentials |
|---|---|
| Opal web interface | http://localhost:8880 (or https://localhost:8843, self-signed certificate) |
| Administrator | administrator / password |
| DataSHIELD user | dsuser / P@ssw0rd |
| Data | project CNSIM, tables CNSIM.CNSIM1 and CNSIM.CNSIM2 |
These credentials are for local development only.
Install the client packages:
install.packages("dsBaseClient", repos = c(getOption("repos"), "https://cran.obiba.org"), dependencies = TRUE)
Then run client/client.R (short check) or client/sandbox.R (longer analysis using both tables). They connect to http://localhost:8880 as dsuser; override with the DS_URL, DS_USER and DS_PASSWORD environment variables.
Copy docker/.env.example to docker/.env and uncomment what you want to change: Opal image, passwords, Java memory, project and table names (CNSIM_PROJECT, CNSIM_TABLES). Each table is downloaded from <CNSIM_BASE_URL>/<table>.csv.
Setup (customise.sh) runs only once per fresh volume, so after changing users, projects or tables, reset first:
docker compose down -v
docker compose up -d
-v deletes the opal-data and mongo-data volumes. Always keep the two together: wipe both or neither.
docker/docker-compose.jaeger.yml adds Jaeger and turns on Opal's OpenTelemetry trace export (obiba/opal#4194):
docker compose -f docker-compose.yml -f docker-compose.jaeger.yml up -d
Run one of the client scripts, then open http://localhost:16686 and select the service opal-local. The override defaults to the obiba/opal:snapshot image; if no traces appear, set OPAL_IMAGE in .env to a build that includes the PR. Without the override nothing is exported. Override files can be combined, e.g. -f docker-compose.yml -f docker-compose.jaeger.yml -f docker-compose.dsgeospatial.yml.
The default stack only has the standard DataSHIELD packages. Extra server-side R packages (such as dsGeospatial) live in a Rock image of their own, added to the stack with a Compose override file, the same way as the Jaeger override above.
How it fits together:
- Opal talks to one or more Rock R servers (
ROCK_HOSTS). Each Rock reports a cluster name, and each cluster is a DataSHIELD profile in Opal. The stock Rock is profiledefault; the dsGeospatial Rock is profileds-geospatial. - The image is built from the package's own repo (its
docker/Dockerfile), not from this one. The Dockerfile installs the package from GitHub, and a build argument chooses the branch, tag or commit. - This repo only contains the override (
docker/docker-compose.dsgeospatial.yml) that starts that image and registers it with Opal.
This clones both repos side by side, builds the image and starts Opal with the extra Rock (needs git and Docker; the first build compiles the spatial libraries and takes a while):
mkdir -p datashield && cd datashield \
&& git clone https://github.com/FederatedMethods/datashield_dev_install \
&& git clone https://github.com/FederatedMethods/dsGeospatial \
&& docker build --platform linux/amd64 -t ds-geospatial-rock:0.0.0.9000 dsGeospatial/docker \
&& cd datashield_dev_install/docker \
&& docker compose -f docker-compose.yml -f docker-compose.dsgeospatial.yml up -d
The image tag must match DSGEOSPATIAL_ROCK_IMAGE (default ds-geospatial-rock:0.0.0.9000). The --platform flag matches the override; the Rock base image is amd64 only, so on Apple Silicon it runs under emulation (slower, but works).
docker/deploy-dsgeospatial.sh repeats the whole sequence and is safe to re-run. It pulls the latest dsGeospatial Dockerfile, rebuilds the image with the newest commit of the chosen version, and recreates only the containers whose image changed. Opal and MongoDB data are kept.
cd datashield_dev_install/docker
./deploy-dsgeospatial.sh # latest main
DSGEOSPATIAL_REF=dev-MoransI ./deploy-dsgeospatial.sh # a branch, tag or commit SHA
The script expects the repos side by side. Set WORKDIR (parent folder) or GEO_DIR (the dsGeospatial clone) if yours are elsewhere. Other settings are environment variables too: DSGEOSPATIAL_ROCK_IMAGE, ROCK_PLATFORM, GEO_URL.
Note that DSGEOSPATIAL_REF chooses which version of the R package goes into the image. The Dockerfile itself comes from whatever branch of the dsGeospatial clone is checked out.
Do this once, and again after rebuilding with a new version, because Opal remembers a profile's published methods in its data volume (no down -v needed).
- Open http://localhost:8880 and log in as
administrator. - Go to Administration > DataSHIELD. A profile for the
ds-geospatialcluster should be listed; if not, add one and choose that cluster. (Menu labels can differ slightly between Opal versions.) - Make sure the profile is started and initialised, so Opal publishes the package's methods (for dsGeospatial:
MeanSdGpDS,groupcorDS,asFactorDS3,MoransI,Localmoran). - If
dsuseris refused on the new profile, check the profile's permissions.customise.shgrants the DataSHIELD use permission globally, not per profile.
Select the profile at login. The {dsGeospatial} analyst package, {dsGeospatialClient}, must be installed on the client side as well.
DSOpal::Opal()
builder <- DSI::newDSLoginBuilder()
builder$append(
server = "study1", url = "http://localhost:8880", user = "administrator",
password = "password", driver = "OpalDriver", profile = "ds-geospatial"
)
logindata <- builder$build()
conns <- DSI::datashield.login(logins = logindata)
DSI::datashield.pkg_status(conns) # dsBase and dsGeospatial should both be listedUse the same pattern for any server-side package:
-
In the package's repo, create a
docker/Dockerfilethat startsFROM datashield/rock-base:<tag>, installs the system libraries the package needs, and installs the package (for GitHub packages,remotes::install_github(repo, ref = ...)with a build argument forref). Use the dsGeospatial Dockerfile as a template. -
Build it:
docker build --platform linux/amd64 -t my-package-rock:local path/to/repo/docker. -
Copy
docker/docker-compose.dsgeospatial.ymltodocker/docker-compose.<name>.ymland change the service name, image and cluster. Give each Rock a unique service name andROCK_ID, and a uniqueROCK_CLUSTER. Add the new host toROCK_HOSTS:services: opal: environment: - ROCK_HOSTS=rock:8085,rock-mypackage:8085 depends_on: rock-mypackage: condition: service_started rock-mypackage: image: my-package-rock:local pull_policy: never platform: linux/amd64 environment: ROCK_ID: my-package-rock-01 ROCK_CLUSTER: my-package -
Start with the extra file and set up the new profile in Opal as above:
docker compose -f docker-compose.yml -f docker-compose.<name>.yml up -d
Each additional override file replaces the ROCK_HOSTS line, so if you combine several, list all the hosts in the last file you pass, or put a single ROCK_HOSTS in .env. Packages that are always used together can instead share one image and one profile.
- No project or user after start-up: read the set-up log with
docker compose exec opal cat /srv/customisation.log. Re-run set-up without wiping data usingdocker compose exec -e FORCE=1 opal bash /customise.sh. - Container logs:
docker compose logs opal(orrock,mongodb). - Fresh start:
docker compose down -v, thendocker compose up -d. If a container was stopped uncleanly (e.g. Ctrl-C ondocker compose up), checkdocker ps -afor leftovers;docker compose uprestarts old containers rather than creating new ones. post_starterrors onup: your Docker Compose is older than 2.30; upgrade it.image ... not foundfor the dsGeospatial Rock: the image has not been built yet (the override never pulls from a registry). Build it, or rundocker/deploy-dsgeospatial.sh.- No
ds-geospatialprofile in Opal: check the Rock is running (docker compose -f docker-compose.yml -f docker-compose.dsgeospatial.yml logs rock-geospatial) and that Opal was started with the override, soROCK_HOSTSlists it. Then add the profile for theds-geospatialcluster. - Package methods missing in the profile: initialise the profile again in Opal after rebuilding the image. Confirm the package is in the container:
docker compose -f docker-compose.yml -f docker-compose.dsgeospatial.yml exec rock-geospatial R -q -e 'library(dsGeospatial)'. - Build fails or is very slow on Apple Silicon: the Rock base image is amd64, so the build runs under emulation. Give Docker more CPU and memory, or build on an amd64 machine.
- Image still has an old version of the package: Docker reused a cached layer. Rebuild with
--build-arg CACHEBUST=$(date +%s)(the script does this for you).
To use this on a remote VM, put a reverse proxy with an SSL certificate in front of Opal:
-
Install nginx (
sudo apt install nginx) and create a certificate. A self-signed one is fine for development: DigitalOcean guide. -
Copy
nginx/datashield1.confto/etc/nginx/sites-available/datashield1and setserver_nameto your host name or IP. -
Enable it and reload:
sudo ln -s /etc/nginx/sites-available/datashield1 /etc/nginx/sites-enabled/datashield1 sudo rm /etc/nginx/sites-enabled/default sudo nginx -t && sudo systemctl reload nginx -
In
docker/docker-compose.yml, uncommentCSRF_ALLOWEDand set it to thehost:portyour browser uses (needed because requests through a proxy can look like cross-site requests). Then recreate Opal:docker compose up -d --force-recreate opal. -
If you use UFW, allow HTTPS:
sudo ufw allow 'Nginx HTTPS'.
Browsers will warn about a self-signed certificate; accept it for development.
- Group permissions do not work as expected, so permissions are granted to individual users.
- Tables are imported with every variable as
decimal, so categorical variables (e.g.GENDER) are numeric rather than factors. Functions that need factors, such asds.table, need the variable converted first (e.g. withds.asFactor).
- DataSHIELD wiki and datashield.org: documentation, tutorials, and the list of available packages
- Opal documentation, including the Python client used by
customise.sh - obiba/docker-opal: the official Opal Docker images and examples
- FederatedMethods/ds_sample_data: the sample data used here
- FederatedMethods/dsGeospatial: the server-side geospatial package, with the Dockerfile for its Rock image
- Jaeger documentation: for exploring traces