Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e65a286
Add 2 news options
XV25 Feb 4, 2025
2426d6f
Add option to directly add devices access inside container (such as w…
XV25 Feb 4, 2025
2a396d3
Small typo correction
XV25 Feb 4, 2025
a6d5afa
Add option to desactivate sudo and apt-get at docker run. Also add po…
XV25 Feb 8, 2025
678d8ef
Add more debugging instructions in readme, manage a bug for too basic…
XV25 Apr 3, 2025
b7e9fd8
Add option to change temporary directory for temp files. It can be us…
XV25 Apr 4, 2025
78d5834
Suppressed option for setting apt-cacher distro (not useful, and sour…
XV25 Apr 4, 2025
5137a5e
Small cleanup
XV25 Apr 4, 2025
c634efa
Quick fix for "--temp-dir" feature (.cid file didn't take into accoun…
XV25 Apr 8, 2025
13509ec
Quick cleanup
XV25 Apr 8, 2025
c524a07
Correct a bug with --other option
XV25 May 22, 2025
e351c3a
Add --ipc==host and --pid==host options (necessary to make container …
XV25 May 23, 2025
9c4110c
Quick modification to be sure that the desactivation of sudo / others…
XV25 May 26, 2025
c6e4d44
simplify readme
ntorresalberto Aug 5, 2025
fc29bf2
improve message when distro check fails
ntorresalberto Aug 6, 2025
5c09b82
comment apt-cacher-ng workaround until confirmed to be the solution
ntorresalberto Aug 6, 2025
edfacae
enable --pid=host --ipc=host by default, disable with --no-pid-ipc-host
ntorresalberto Aug 6, 2025
167549e
remove noSetupSudo flag (redudant with noUser) and fix some lint errors
ntorresalberto Jan 15, 2026
62def9f
default to --pid=host --ipc=host and add --no-pid-ipc-host
ntorresalberto Jan 15, 2026
fd28ed5
remove --other, redundant with usage dogi run <image> -- arg1 arg2
ntorresalberto Jan 15, 2026
6840e4c
update deps
ntorresalberto Jan 16, 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
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ It allows using rootless containers, running GUIs, quickly mounting your current

Even though **dogi** was originally inspired by [rocker](https://github.com/osrf/rocker) and solves a similar problem (or the same), it aims to do so with minimum user effort. Additionally, it provides the ability to interact with the `docker` client directly ([transparent](#design-principles)).

## Quickstart

```bash
# install binary
Expand All @@ -35,6 +34,22 @@ Some [optional setup steps](#optional-setup-steps) might be required.

**NOTE:** You can also install from source: `CGO_ENABLED=0 go install -a github.com/ntorresalberto/dogi@latest`

### Requirements

**dogi** relies on the docker engine CLI for its operations. Before using dogi, make sure:

* You have installed [docker engine via the official guide](https://docs.docker.com/engine/install/ubuntu/). Docker installed through snap won't work, because **dogi** sometimes creates files and uses `/tmp`.
* You have the correct permissions to call docker cli withotu sudo. This can be setup with the [post-installation steps](https://docs.docker.com/engine/install/linux-postinstall/).
* In order to use docker (and **dogi**) with GPU support, you must first [follow the installation prerequisites instructions](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#prerequisites), and the [configuration instructions](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#configuration). Otherwise, the following bug will appears :

```
docker : Error response from daemon: unknown or invalid runtime name: nvidia
```

## Quickstart



---

- [Examples](#examples)
Expand Down Expand Up @@ -115,7 +130,7 @@ You should find **dogi** useful if you:

- **transparent**: **dogi** forwards any unrecognized arguments to docker, in case you ever need to do anything not currently supported.
- **simple**: aims to cover the most common use cases with the least user intervention (you shouldn't need to pass any extra flags/options most of the time). If you don't agree with the defaults, [please say so](https://github.com/ntorresalberto/dogi/issues/new).
- **secure**: there are [many ways](http://wiki.ros.org/docker/Tutorials/GUI) to expose the xorg server to containers, **dogi** tries to do it in the most secure way. Additionally, it proposes an easy way to avoid the potentially dangerous practice of root containers.
- **secure**: there are [many ways](http://wiki.ros.org/docker/Tutorials/GUI) to expose the xorg server to containers, **dogi** tries to do it in the most secure way. Additionally, it proposes an easy way to avoid the potentially dangerous practice of root containers.
- **minimalist**: **dogi** thrives to have the least amount of dependencies and not do more than it needs.

> Many (open source) hackers are proud if they achieve large amounts of code, because they believe the more lines of code they've written, the more progress they have made. The more progress they have made, the more skilled they are. This is simply a delusion.
Expand All @@ -124,7 +139,8 @@ You should find **dogi** useful if you:

### Limitations

- Only supports ubuntu-based images (because of apt commands used)
- Only supports debian-based images like ubuntu (because of apt commands used) and more recently fedora.

- Only supports X11 environments for GUI applications (because of xorg socket communication)

<hr style="border:4px solid blue">
Expand Down Expand Up @@ -174,4 +190,3 @@ source .bashrc

This error is usually caused by a container running an older version of glibc than your host system (where you compiled `dogi`).
A possible cause of this is you didn't use `CGO_ENABLED=0` in the `go install`, as specified in #quickstart.

12 changes: 11 additions & 1 deletion assets/apt-cacher/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,19 @@
# add this to container:
# echo 'Acquire::http { Proxy "http://localhost:3142"; };' >> /etc/apt/apt.conf.d/01proxy
# echo 'Acquire::http { Proxy "http://172.17.0.2:3142"; };' >> /etc/apt/apt.conf.d/01proxy
FROM ubuntu:22.04
FROM ubuntu:22.04

VOLUME ["/var/cache/apt-cacher-ng"]

# TODO left commented until we can confirm this is a workaround
# ## Added : Try to clean everything (for bug : apt-get BADSIG GPG)
# RUN apt-get clean && \
# cd /var/lib/apt &&\
# mv lists lists.old &&\
# mkdir -p lists/partial && \
# apt-get clean &&\
# apt-get update -qq -y

RUN apt-get update && apt-get install -y apt-cacher-ng \
&& sed -i 's/\# PassThroughPattern: \.\*/PassThroughPattern: \.\*/g' /etc/apt-cacher-ng/acng.conf

Expand Down
6 changes: 5 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,12 @@ var (
noUSBPtr bool
noNethostPtr bool
noCacherPtr bool
noPIDIPCHostPtr bool
workDirPtr string
contNamePtr string
devAccPtr string
devRMWPtr string
tempDirPtr string
logger = log.New(os.Stdout, appname+": ", log.Lmsgprefix)
dockerRunArgs = []string{
"--interactive",
Expand Down Expand Up @@ -186,7 +190,7 @@ Examples:

func panicKey(key string, mapWithoutKey map[string]string) {
if _, ok := mapWithoutKey[key]; ok {
panic(fmt.Errorf("%s should not exist in this dictionary\n", key))
panic(fmt.Errorf("%s should not exist in this dictionary", key))
}
}

Expand Down
68 changes: 60 additions & 8 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@ func imageDistro(imageName string) string {
out, err := exec.Command("docker", "run", "--rm", "--tty",
"--entrypoint=cat",
imageName, "/etc/os-release").Output()
if err != nil {
logger.Println("Error: failed to verify image distro:")
logger.Printf("image: %s\n", imageName)
logger.Printf("error: %s\n", err.Error())
logger.Printf("Please share this log the %s devs at:\n", appname)
logger.Fatalf("https://github.com/ntorresalberto/dogi/issues/new")
}
check(err)

for _, val := range supportedDistros() {
Expand All @@ -248,10 +255,9 @@ func setAptCacher() string {
{
logger.Printf("build apt cacher image: %s\n", imgName)
// build apt-cache-ng image
dir, err := os.MkdirTemp("", "dogi_apt-cache")
//dir, err := os.MkdirTemp("", "dogi_apt-cache")
dir, err := os.MkdirTemp(tempDirPtr, "dogi_apt-cache")
check(err)
defer os.RemoveAll(dir) // clean up

tmpfn := filepath.Join(dir, "Dockerfile")
check(os.WriteFile(tmpfn, []byte(assets.AptCacheDockerfile), 0666))
logger.Printf("temp dir: %s\n", dir)
Expand All @@ -265,6 +271,8 @@ func setAptCacher() string {
fmt.Println(string(out))
panic(err)
}

check(os.RemoveAll(dir)) // clean up
}

// launch apt-cacher container
Expand Down Expand Up @@ -311,8 +319,9 @@ func setAptCacher() string {
}

// find out apt-cacher ip
// is it possible to have multiple IPs for this container?
out, err := exec.Command("docker", "container",
"inspect", "-f", "{{ .NetworkSettings.IPAddress }}", contName).Output()
"inspect", "-f", "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}", contName).Output()
if err != nil {
logger.Printf("container %s not found, launching...", contName)
_, err = exec.Command("docker",
Expand All @@ -337,7 +346,7 @@ func setAptCacher() string {

aptCacherConf := fmt.Sprintf("Acquire::http { Proxy \"http://%s:3142\"; };", ip)

aptCacherFile, err := os.CreateTemp("", fmt.Sprintf(".%s_%s_*", appname, baseName))
aptCacherFile, err := os.CreateTemp(tempDirPtr, fmt.Sprintf(".%s_%s_*", appname, baseName))
check(err)
logger.Printf("apt-cacher file: %s", aptCacherFile.Name())
check(os.WriteFile(aptCacherFile.Name(), []byte(aptCacherConf), 0666))
Expand Down Expand Up @@ -478,6 +487,11 @@ const runExamples = `
- Launch an 3D accelerated GUI (opengl)

{{.appname}} run ubuntu -- bash -c "sudo apt install -y mesa-utils && glxgears"

- Add access to a webcam (ex : /dev/video0) :

{{.appname}} run ubuntu --device-access "/dev/video0"

`

var (
Expand Down Expand Up @@ -529,8 +543,14 @@ Examples:
bashCmdPath, err := exec.LookPath("bash")
check(err)

// if tempdir is not provided, use OS default
if tempDirPtr == "" {
tempDirPtr = os.TempDir()
}

// create xauth magic cookie file
xauthfile, err := os.CreateTemp("", fmt.Sprintf(".%s*.xauth", appname))
xauthfile, err := os.CreateTemp(tempDirPtr, fmt.Sprintf(".%s*.xauth", appname))

check(err)
logger.Println("temp xauth file:", xauthfile.Name())
addCopyToContainerFile(xauthfile.Name(), "/.xauth")
Expand Down Expand Up @@ -559,7 +579,7 @@ Examples:
logger.Printf("workdir: %s\n", workDirPtr)
mountStrs := []string{fmt.Sprintf("--volume=%s:%s", workDirPtr, workDirPtr)}

cidFile := fmt.Sprintf("%s/.%s%v.cid", os.TempDir(), appname, rand.Int63())
cidFile := fmt.Sprintf("%s/.%s%v.cid", tempDirPtr, appname, rand.Int63())
mountStrs = append(mountStrs, fmt.Sprintf("--cidfile=%s", cidFile))
mountStrs = append(mountStrs, fmt.Sprintf("--volume=%s:%s", cidFile, cidFileContainer))

Expand Down Expand Up @@ -619,6 +639,13 @@ Examples:
dockerRunArgs = append(dockerRunArgs, "--network=host")
}

if !noPIDIPCHostPtr {
// useful for https://github.com/eProsima/Fast-DDS/issues/2956
logger.Println("add --pid=host --ipc=host, to disable use --no-pid-ipc-host")
dockerRunArgs = append(dockerRunArgs, "--pid=host")
dockerRunArgs = append(dockerRunArgs, "--ipc=host")
}

if privilegedPtr {
logger.Println("adding --privileged")
dockerRunArgs = append(dockerRunArgs, "--privileged")
Expand Down Expand Up @@ -708,6 +735,24 @@ Examples:
"--volume=/dev/bus/usb:/dev/bus/usb")
dockerRunArgs = append(dockerRunArgs,
"--device-cgroup-rule=c 189:* rmw")

// add commands to add rules to specific usb devices (as stated by https://stackoverflow.com/a/62758958)
if devRMWPtr != "" {
var indexes = strings.Split(devRMWPtr, ";")
for i := 0; i < len(indexes); i++ {
var s = "--device-cgroup-rule=c " + indexes[i] + ":* rmw"
dockerRunArgs = append(dockerRunArgs, s)
}
}
// add rules to mount specific usb devices
if devAccPtr != "" {
var indexes = strings.Split(devAccPtr, ";")
for i := 0; i < len(indexes); i++ {
var s = "--device=" + indexes[i]
dockerRunArgs = append(dockerRunArgs, s)
}
}

}

if !noUserPtr && userObj.Uid == "0" {
Expand Down Expand Up @@ -735,7 +780,9 @@ Examples:

// TODO: createUser file won't be removed because
// process is replaced at Exec, is there a way?
createUserFile, err := os.CreateTemp("",
// createUserFile, err := os.CreateTemp("",
// fmt.Sprintf(".%s*.sh", appname))
createUserFile, err := os.CreateTemp(tempDirPtr,
fmt.Sprintf(".%s*.sh", appname))
check(err)
logger.Println("create user script:", createUserFile.Name())
Expand Down Expand Up @@ -813,4 +860,9 @@ func init() {
runCmd.Flags().BoolVar(&noRMPtr, "no-rm", false, "don't launch with --rm (container will exist after exiting)")
runCmd.Flags().BoolVar(&noUSBPtr, "no-usb", false, "don't mount usb devices")
runCmd.Flags().BoolVar(&noNethostPtr, "no-nethost", false, "don't launch with --network=host")
runCmd.Flags().StringVar(&devRMWPtr, "device-rmw", "", "add rmw rules to the following devices (as stated in https://stackoverflow.com/a/62758958). Format : <id_dev_a>;<id_dev_b>")
runCmd.Flags().StringVar(&devAccPtr, "device-access", "", "mount the following devices to container (through --device option). Format : <dev_name_a>;<dev_name_b>")
runCmd.Flags().StringVar(&tempDirPtr, "temp-dir", "", "temporary directory to use for dogi (default: $TMPDIR or /tmp, through empty command). Can be modified if there are access issues with this particular folder.")
runCmd.Flags().BoolVar(&noPIDIPCHostPtr, "no-pid-ipc-host", false, "don't launch with --pid=host --ipc=host.")

}
Loading