This project provides documentation, configuration samples and tools to automate the deployment of an online experiment that relies on DuckSoup for its videoconferencing part.
The main components of the stack are Linux, nginx and Docker (including a published image of DuckSoup).
We'll use the following terms below:
- host: the server that runs and exposes the app
- service: an independent Docker container (for instance DuckSoup or postgres) run on the host by Docker Compose
- profile: a group of dependent services which are jointly needed to provide a given utility (for instance the
monitoring
profile relies on thegrafana
andprometheus
services, but theducksoup
profile only relies on theducksoup
service) - app: the online experiment, an end-user application made of one or more profiles
As a by-product, a minimal oTree webapp is shipped in examples/experiment
to complete the app. But more generally, the intent of this project is to provide enough information about DuckSoup and Docker so that you may enhance or replace parts/services to best fit your needs (using oTree, or not).
Running the different services (oTree, the database, DuckSoup...) that make the overall application is managed by Docker Compose. Though this project heavily relies on Docker and Docker Compose, it's fine to build DuckSoup from source and to prefer a different deployment method than the one described below.
Moreover, this project illustrates a contained approach by running all the services on the same host with a unique docker-compose.yml
definition file. Relying on Docker or not, it's also fine to prefer an architecture where DuckSoup is run on its own host (possibly shipped with a GPU to help with video encoding) whereas one or more experiments use the DuckSoup instance as-a-service from other hosts/origins (in that case the origins have to be declared/granted by setting the DUCKSOUP_ALLOWED_WS_ORIGINS
environment variable of DuckSoup, see more here).
The process of installing and running DuckSoup can be broken down as:
- provisioning the host: install software to deploy, run and manage DuckSoup on a server (or developer machine)
- host configuration: create needed (Linux) users and configuration software installed during the previous step
- app configuration: clone this project and a few binaries (additional GStreamer plugins to enhance DuckSoup processing options), set environment variables and edit DuckSoup configuration files
- deployment: update and run the app whenever a new version needs to be deployed
- usage: how to use the app
This documentation showcases the deployment of an app made of three parts/profiles:
- DuckSoup, a videoconferencing tool for online social experiments (profile
ducksoup
) - a minimal experiment based on oTree that uses DuckSoup (and a service called mastok that manages users before arrival in the experiment) (profile
social
) - a server monitoring tool based on Grafana and Prometheus (profile
monitoring
)
Please note that DuckSoup has no dependency on oTree: DuckSoup is called client-side by the experiment and thus has no prerequesites regarding the technologies involved to develop the experiment server-side. This example experiment is only provided to illustrate the deployment of a full app.
These instructions apply to a Debian-based server, but can be adapted to other distributions.
Install the following dependencies on the host:
- Docker
- Docker Compose V2
- nginx to bind server blocks to services/apps managed by docker compose, and to handle SSL
- certbot if needed for SSL certificates
- optional (not documented): a firewall to control exposed ports (ufw for instance), a backup service, logrotate to rotate logs generated by DuckSoup and GStreamer
The app is installed and run by Docker Compose (see below).
Skip this paragraph if you don't use a NVIDIA GPU for video encoding
If you plan to use a GPU from a Docker container you'll need to follow these instructions:
-
NVIDIA driver: first check if it is already installed (try
nvidia-smi
), if not search for available versions (apt-cache search nvidia-driver
) and install (for instance withapt-get install nvidia-driver-460
) -
NVIDIA and Docker: this description explains installing
nvidia-container-runtime
is sufficient to have Docker containers benefit from NVIDIA GPUs. To do so, update your host repository configuration following these instructions andapt-get install nvidia-container-runtime
-
Restart Docker (
systemctl restart docker
) -
Check it worked following this tutorial, trying to run
examples/docker-compose.nvidia-smi.yml
or try directly:
docker run -it --rm --gpus all ubuntu nvidia-smi
And check GPUs are listed.
- User
Create a deploy
user that will run the app.
- Security
Choose a ssh login policy and security settings (for instance using fail2ban) for the deploy
user.
Ports need to be opened for SSH connections, nginx (80, 443) and UDP ephemeral ports. You may manage this through firewall settings (not documented).
- Docker
You may change docker default network configuration if you don't want its default subnet to conflict with others (172.20.*). If so copy, paste and edit the contents of examples/docker/daemon.json
to /etc/docker/daemon.json
and then restart docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
- Nginx and Let's Encrypt
Declare a new nginx server block as /etc/nginx/sites-available/ducksoup
from the examples/nginx/ducksoup-host.com
file:
-
edit
server_name
to match with your domain name -
the
upstream
servers declare services running on the host (matching their ports with the ones defined indocker-compose.yml
) -
the
location
directives bind path prefixes to corresponding services (for instance binds/otree
to the oTree application) and enable/configure a few options (for instance WebSockets) -
about the proxy timeout settings: by default WebSockets are closed after a default timeout of 1 minute if no message was exchanged during this period (resulting in DuckSoup user sessions to be terminated).
Enable this server block and run certbot to enable https (a DuckSoup requirement):
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/ducksoup-host.com /etc/nginx/sites-enabled/
sudo systemctl reload nginx
# run certbot
sudo certbot --nginx -d ducksoup-host.com
# test
sudo certbot renew --dry-run
Check modifications in /etc/nginx/sites-available/ducksoup-host.com
, it should look like examples/nginx/ducksoup-host.com.certbot
.
Alternatively for local/development purposes only, you may:
-
edit your
/etc/hosts
file to declare a local domain name -
create locally-trusted certificates (for instance with mkcert) for this local domain name
-
edit
/etc/nginx/sites-available/ducksoup
to use these certificates (checkexamples/nginx/ducksoup-host.com.certbot
to see how)
And finally restart nginx:
sudo systemctl reload nginx
- Monitoring and firewall
If you are using the monitoring
profile, one of its services (prometheus
) needs to access the host (since it regularly calls node_exporter
which in turn needs to be run on the host network). If you use a firewall, networking from containers to host may be blocked and thus monitoring won't work (prometheus
won't be able to call node_exporter
on the host port 9100 or dcgm_exporter
on the host port 9400).
Here is an example to relax the firewall (ufw) to allow traffic coming from a given subnet (see configuration declared in examples/docker/daemon.json
) where prometheus
is run, towards ports 9100 and 9400 on the host:
sudo ufw allow from 172.80.0.1/16 to any port 9100
sudo ufw allow from 172.80.0.1/16 to any port 9400
- Rotating logs
You may check the examples/logrotate/ducksoup
configuration file and adapt it to your needs before creating in the host folder /etc/logrotate.d/
In particular you may have to change the target location of logs to be rotated (/home/deploy/deploy-ducksoup/app/log/ducksoup/*.log
in the example) depending on where the log volume is mounted on the host.
Then try a dry-run with:
sudo logrotate /etc/logrotate.conf --debug
This project comes with an examples
folder to share a few example files, but when it comes to configuring and deploying the app, all commands are meant to be executed in the main app
folder.
Switch to the deploy
user, clone this repository and change directory:
su deploy
git clone https://github.com/ducksouplab/deploy-ducksoup.git
cd deploy-ducksoup
Since deployment and running is managed by Docker Compose, most of the configuration is done in docker-compose.yml
. From a broad perspective this file:
-
declares services and groups them in profiles, enabling to start/stop them individually or by profile
-
for each service, it defines:
- how to instantiate the service (from a prebuilt Docker image or following build instructions in a Dockerfile)
- the port this service is running on (being publicy exposed or not depends on the nginx configuration, except for this famous bug)
- how some folders on the host are mounted as volumes in the service/container
- what environment variables are propagated from the host to the services (and renaming a few); some are required for the app to work (see Environment variables)
The app
folder contains the following files:
- a base
docker-compose.yml
that declares and configures services; you may edit this file or prefer using an override (see next bullet) to fit your needs docker-compose.override-example.*.yml
can be used as an example to create adocker-compose.override.yml
(to override existing services or even declare new ones, without editingdocker-compose.yml
)env.example
can be used as an example to create a.env
file that will be automatically loaded by Docker Compose to define environment variables (made available todocker compose
commands, and also forwarded to containers depending theenvironment:
section of each service)appctl
is a helper script that shortens a few frequent Docker Compose commands
Then, by convention (of this project), app
subfolders are meant to be mounted as Docker volumes:
config
contains configuration filesdata
(needs to be writable by thedeploy
user) contains data created by the app that needs to be persisted between restartslog
(needs to be writable by thedeploy
user) contains logs produced by servicesplugins
(only for DuckSoup service) is used to enhance DuckSoup with additional GStreamer plugins (see how here)
When cloning this repository the config/prometheus
folder is created (and contains a prepared configuration file), but config/ducksoup
, plugins
and data
needs to be created and owned by deploy:deploy
:
cd app
mkdir -p config config/ducksoup data/db data/ducksoup data/grafana data/prometheus log/ducksoup plugins
chown -R deploy:deploy config data log plugins
chmod 770 -R data log
Old?: the bound data/db
volume is chmoded as 700 due to https://github.com/docker-library/postgres/blob/master/docker-entrypoint.sh#L39
The previous layout (having folders under app
being used as volume sources) provides with a simple working example. You may want to change this setup (check the documentation for more information) and use other locations, for instance under host-mounted hard drives. If you do, prefer overriding the default setup in docker-compose.override.yml
.
Create the docker-compose.override.yml
file by copying one of docker-compose.override-example.*.yml
: it specifies how to build the image needed for a default experiment service (the build option has not been defined in docker-compose.yml
).
An alternative would be to define an image:
property (in docker-compose.override.yml
) to pull a published image.
A .env
file is needed to provide the different services with appropriate configuration. Create it (by copying env.example
) and then edit it:
cp env.example .env
nano .env
You should at least:
- define
DOCKER_UNAME
,DOCKER_UID
andDOCKER_GID
: they are used to define the user that launches the different services. If you follow this documentation setup,DOCKER_UNAME
isdeploy
,DOCKER_UID
is the output ofid deploy -u
andDOCKER_GID
is the output ofid deploy -g
. If you prefer another user, it should own folders mounted as volumes to allow the containers to write to them (check app/ folder layout and in particular thechown
command) - change secrets
(regarding 1., please note it could be possible to define different users for different services by modifying the user:
section of each service in docker-compose.yml
or docker-compose.override.yml
)
You may also change other variables, like ports (depending on your nginx configuration) or service settings. Indeed, when runnning the app (see below, through docker compose
or appctl
) the .env
file is automatically loaded.
Here are the environment variables you may edit, grouped by service:
ducksoup
service (only relevant variables are described here, please refer to the Ducksoup documentation for an exhaustive list):DUCKSOUP_PORT
: port listened by DuckSoup (nginx proxies to this port)DUCKSOUP_WEB_PREFIX
, DuckSoup web server and signaling prefix:- leave empty if DuckSoup is available at
https://ducksoup-host
DUCKSOUP_WEB_PREFIX=/path
if DuckSoup is available athttps://ducksoup-host/path
(the nginx configuration would then proxy DuckSoup in alocation /path {...}
block)
- leave empty if DuckSoup is available at
DUCKSOUP_EXPLICIT_HOST_CANDIDATE
if true, will useDUCKSOUP_PUBLIC_IP
as a host candidate during signaling (not necessary if ICE servers are used)DUCKSOUP_PUBLIC_IP
if set, will be used to add a Host candidate during signaling (not necessary if ICE servers are used)DUCKSOUP_ALLOWED_WS_ORIGINS
: origins trusted by DuckSoup (you need to add DuckSoup itself, for instancehttps://ducksoup-host
, if you want to use test pages like https://ducksoup-host/test/mirror/, or other origins if experiments are served from other domains)DUCKSOUP_TURN_ADDRESS
andDUCKSOUP_TURN_PORT
(defaults to none) if both are set, they will be used to configure DuckSoup embedded TURN server and share its configuration with ducksoup.js asDUCKSOUP_TEST_LOGIN
: basic auth login for DuckSoup test pagesDUCKSOUP_TEST_PASSWORD
: basic auth password for DuckSoup test pagesDUCKSOUP_NVCODEC
: use NVIDIA hardware for H264 encoding and decoding (only if GPU available on host)DUCKSOUP_NVCUDA
: use NVIDIA hardware for video conversion (only if NVIDIA GPU available on host)DUCKSOUP_JITTER_BUFFER=200
(defaults to 150, in milliseconds) latency value for the RTP jitter buffer of incoming tracksDUCKSOUP_GENERATE_PLOTS=true
(defaults to false) generates debug plots in the interaction data folderDUCKSOUP_GENERATE_TWCC=true
(defaults to false) enables RTCP TWCC reports generated by DuckSoup and sent to browserDUCKSOUP_GCC=true
(defaults to false, meaning bandwith estimation is done relying on RTCP Receiver Reports) enables GCC bandwidth estimationDUCKSOUP_GST_TRACKING=true
(defaults to false) enabled GStreamer log processing (you are most likely not interested in that option)DUCKSOUP_LOG_STDOUT=true
(defaults to false, except whenDUCKSOUP_MODE=DEV
) to print logs to Stdout:- if
DUCKSOUP_LOG_FILE
is also set, logs are written to both - if neither are set, logs are written to Stderr
- if
DUCKSOUP_LOG_FILE
declares a file to write logs to (fails silently if file can't be opened) (file path from container viewpoint)DUCKSOUP_LOG_LEVEL
(defaults to 2) selects log level display (see next section)DUCKSOUP_INTERCEPT_GST_LOGS
(defaults to false) disable GStreamer default logger to intercept logs and put them in the relevant interaction logs if possibleDUCKSOUP_FORCE_OVERLAY
displays a time overlay in videos (recorded)DUCKSOUP_NO_RECORDING
disables audio/video file recordingsDUCKSOUP_STUN_SERVER_URLS=false
(defaults tostun:stun.l.google.com:19302
) declares comma separated allowed STUN server URLs to be used to find ICE candidates (or false to disable STUN)GST_DEBUG
controls GStreamer debug output format as explained here,GST_TRACERS
andGST_DEBUG_FILE
are also exposedPION_LOG_TRACE
(unset by default) logs pion debug messages (see more)DUCKSOUP_CONTAINER_STDOUT_FILE
(not of interest to DuckSoup itself, but has an effect on its container'sCMD
) writes Stdout to the specified fileDUCKSOUP_CONTAINER_STDERR_FILE
(not of interest to DuckSoup itself, but has an effect on its container'sCMD
) writes Stderr to the specified file
postgres
service:POSTGRES_PASSWORD
PostgreSQL password also known by the experiment service inOTREE_DATABASE_URL
otree
service:OTREE_PORT
oTree experiment running port as defined in nginx host proxyOTREE_DATABASE_URL
PostgreSQL connection URLOTREE_ADMIN_PASSWORD
password of the oTree admin web interfaceOTREE_DUCKSOUP_URL
DuckSoup root URL, for instancehttps://ducksoup-host/
(with trailing slash)OTREE_DUCKSOUP_FRONTEND_VERSION
JS/CSS ducksoup assets version- these variables define DuckSoup interaction configuration as requested by oTree for all its experiments:
OTREE_DUCKSOUP_REQUEST_GPU
(defaults to false),OTREE_DUCKSOUP_FRAMERATE
(deffault to 30),OTREE_DUCKSOUP_WIDTH
(defaults to 800),OTREE_DUCKSOUP_HEIGHT
(defaults to 600),OTREE_DUCKSOUP_FORMAT
(defaults to H264, VP8 being the only other option)
mastok
service:MASTOK_PORT
to set the port Mastok listens toMASTOK_ORIGIN
to set what origin is trusted for WebSocket communication. If Mastok is running on port 8190 on localhost, but is served (thanks to a proxy) and reachable at https://mymastok.com, the validMASTOK_ORIGIN
value ishttps://mymastok.com
MASTOK_WEB_PREFIX
if Mastok is served under a prefix pathMASTOK_LOGIN
andMASTOK_PASSWORD
to define login/password for HTTP basic authenticationMASTOK_DISABLE_LIVE_REDIRECT
(defaults to false) to prevent mastok for redirecting same participant (= same JS fingerprint) to a session they have joined if it is still runningMASTOK_DATABASE_URL
to connect to the databaseMASTOK_OTREE_PUBLIC_URL
(likehttp://host.com/otree
) to reach oTree public pagesMASTOK_OTREE_API_URL
(likehttp://localhost:8180
) to reach oTree REST API- please note having two
MASTOK_OTREE_*_URL
may be useful if Mastok connects to the REST API in a different way than the clients (participants), but they should point to the same running oTree instance MASTOK_OTREE_API_KEY
to authenticate to oTree API
grafana
service:GF_PORT
grafana running port as defined in nginx host proxyGF_PATH
grafana path prefix as defined in nginx host (set to/grafana
if entry point ishttps://ducksoup-host/grafana
)GF_PASSWORD
grafana admin password to access web interface
Caution: GF_PASSWORD
needs to be set before launching monitoring with docker compose
or appctl
. Indeed it is used as an initial value, and then stored in grafana mounted volume. If not set, the default grafana password
will be used, and later use of GF_PASSWORD
will be ignored until the mounted volume is persisted.
Docker Compose is used to run and manage (for instance update and automatically restart) the app. The app is broken down as profiles
in docker-compose.yml
:
- the
ducksoup
profile defines DuckSoup - the
social
profile defines 3 services:mastok
a service that routes users tootree
, an example web app that uses DuckSoup (client-side) and relies on adb
, a PostgreSQL database - the
monitoring
profile defines a monitoring utility that relies on Grafana and Prometheus, to display information about the server state
You can run profiles independently from each other: you may for instance run only DuckSoup and the monitoring service if the experiment is hosted on another server.
Let's see a few available Docker and Docker Compose commands:
# nota bene: commands below are to be executed by the deploy user in the app/ folder
# and the .env file should specify this user as stated above
# retrieve the latest Docker images (used to instantiate services)
docker compose pull <service_name>
# run all profiles
docker compose up -d --build
# run a given profile
docker compose --profile ducksoup up -d --build
# clean/prune Docker images
docker image prune -f
The appctl
script is a helper script that shortens docker compose
commands. To enable it:
chmod u+x appctl
export PATH="$PATH:`pwd`"
Usage by profile:
# nota bene: commands below are to be executed by the deploy user in the app/ folder
# and the .env file should specify this user as stated above
# (re)build and start services with given profile
appctl up ducksoup
appctl up monitoring
appctl up experiment
# if you want to enable nvidia metrics in monitoring
appctl up nvidia
# stop profiles
appctl stop <profile_name>
# reloading is enough (as opposed to `up`) if...
# ...only configuration files have changed, like .env or files in the config folder
appctl reload <profile_name>
Usage by service:
# pull latest ducksoup image (from docker hub)
appctl pull <service_name>
# enter a running container (to inspect/debug) by service name
appctl sh <service_name>
One special feature, only for ducksoup
: when the service is launched the first time, the config/ducksoup
folder of the host is populated with /app/config
from the container, thanks to a Docker "named volume". It enables editing config files (for instance the minimal video bitrate for reencoded tracks, in config/ducksoup/sfu.yml
) and reloading ducksoup
with this new configuration:
nano config/ducksoup/gst.yml
appctl reload ducksoup
One caveat of this approach is that when a new image of DuckSoup is pulled (with appctl pull ducksoup
) and the ducksoup
service is rebuild and restarted (with appctl up ducksoup
) the config/ducksoup
already exists and won't be modified even if the new image contained modifications. That's why you have the choice between two deployment methods:
Method #1 (keep current config/ducksoup
contents on the host and mount them in the ducksoup
service):
appctl pull ducksoup
appctl up ducksoup
Method #2 (reset config/ducksoup
contents with the latest DuckSoup image contents):
appctl pull ducksoup
# answer 'y' to delete `config\ducksoup` contents
appctl reset-config ducksoup
appctl up ducksoup
(one may finally save changes before reset-config
and decide how to merge files)
Ensure you've followed Optional: installation for NVIDIA GPU, then two actions are needed:
- enable the GPU capability in Docker context: copy the contents of
examples/docker-compose.override-example.gpu.yml
inapp/docker-compose.override.yml
(to be created if not already) - start DuckSoup with
DUCKSOUP_NVCODEC=true
in the.env
file (see Environment variables)
Put any additional GStreamer plugins (or dynamic libraries, compiled for the DuckSoup debian target) in the plugins
host folder, to have them available to DuckSoup.
Indeed this folder is mounted as /app/plugins
in the container, which is listed in the image environment variables GST_PLUGIN_PATH
and LD_LIBRARY_PATH
.
Some services are based on prebuilt/published Docker images (like ducksouplab/ducksoup:latest
or postgres:13
) and can only get latest developments when images are updated.
The experiment example image is built locally from the examples/otree/Dockerfile
each time you appctl up experiment
(but it won't be rebuilt by appctl reload experiment
).
It is also possible to build an image by specifying the git repository and branch of a project (check examples/docker-compose.ds-from-source.yml
to see how). In that case, push first to the aforementioned branch and repository, before rebuilding the image (which is done by appctl up
). For private git repository, check the appropriate documentation (on github or gitlab for instance) about how to authenticate the deploy
user to this service, typically by adding and using a SSH key when pulling (the pull being triggered silently by Docker Compose).
Once a new version of the targeted service has been published (be it a Docker image or a project with a Dockerfile), here is how to update and run the service:
ssh deploy@<server>
cd <path to app/>
# pull latest DuckSoup image and start service
appctl pull ducksoup
# resetting config is optional, see above
appctl reset-config ducksoup
appctl up ducksoup
For each service listed below, two example links are given:
- if run on your developer machine, refer to the port declared in the
.env
file, for instance http://localhost:8180/ for otree - if run on a server accessible at
ducksoup-host.com
and behind a nginxlocation
as described previously, the link would be https://ducksoup-host.com/otree
Please refer to DuckSoup documentation.
A mirror test page is available at:
- developer machine: http://localhost:8100/test/mirror/
- server: https://ducksoup-host.com/test/mirror/
When asked to authenticate (HTTP basic auth), use the credentials defined in .env
: DUCKSOUP_TEST_LOGIN
and DUCKSOUP_TEST_PASSWORD
.
Code to be open soon.
The demo is available at:
- developer machine: http://localhost:8190/
- server: https://ducksoup-host.com/mastok/
Please refer to oTree documentation.
The demo is available at:
- developer machine: http://localhost:8180/
- server: https://ducksoup-host.com/otree/
Authenticate with the login admin
and the password defined as OTREE_ADMIN_PASSWORD
in .env
, then:
- click
mirror
underDemo
- click the available singe-use link (it's possible to create new links with
New
in the tab bar) - otree an app made of three steps: an introductory page that collects your name, a page embedding DuckSoup with a pitch audio effect that redirects automatically after 20 seconds to an ending page displaying your name
Gotcha regarding database migration:
The column _created
on the table otree_session
should be of type integer (and not timestamp without a timezone), if it happens, one solution could be to otree resetdb
(but there will be data loss). Leaving a timestamp type for _created
will prevent mastok from creating otree sessions through its API. This type seems to be dependent on oTree version, if not 5.10.3, this should be checked.
Please refer to Grafana documentation.
Grafana is available at:
- developer machine: http://localhost:3000/
- server: https://ducksoup-host.com/grafana/
Once up and running, Grafana needs to be configured to collect and display data:
- under the
https://ducksoup-host.com/grafana/datasources
page, add prometheus as a data source. Since prometheus is running within a container, you need to use the service name as the host in theHTTP>URL
field: if prometheus is running on port 9090, enterhttp://prometheus:9090
and clickSave & Test
- under the https://ducksoup-host.com/grafana/dashboards page, click
Import
and add the following ids in the fieldImport via grafana.com
: 1860 (then clickLoad
and choosePrometheus
as the data source) and 12239 (then clickLoad
)
For more information check Node Exporter Full and NVIDIA DCGM Exporter Dashboard documentation.
Logs generated by DuckSoup (including errors, info... depending on log options set in environment variables, see above) are stored according to DUCKSOUP_LOG_FILE
.
If a GStreamer crash happens, it won't be caught by DuckSoup but will be saved in DUCKSOUP_CONTAINER_STDERR_FILE
. The format of this file is simply:
- log datetime on a new line when DuckSoup starts
- log whatever is printed to Stderr inside the container, including a possible crash
It means the datetime does not refer to the crash, but to the last time DuckSoup started.
List containers:
docker ps
Display latest/live logs:
docker logs <container_id>
# follow in real time
docker logs <container_id> -f
# show tail
docker logs <container_id> --tail N
Connect to a container:
appctl sh <service_name>
docker exec -it <container_id> sh
Create a special image to debug network issue (if any) between containers:
# from project root folder
docker build -f examples/docker/Dockerfile.inspect -t inspect:latest .
docker run --rm -it --add-host=host:host-gateway inspect:latest sh
docker run --rm -it --network=host inspect:latest sh
dcgm
needs to be run as root and not restarted automatically (docker-compose.yml
setting) in case there is no GPU or the GPU is not compatible with dcgm NVIDIA image.