██▓     █    ██  ██ ▄█▀ ███▄ ▄███▓     ▓█████▄ ▓█████ ██▒   █▓
▓██▒     ██  ▓██▒ ██▄█▒ ▓██▒▀█▀ ██▒     ▒██▀ ██▌▓█   ▀▓██░   █▒
▒██░    ▓██  ▒██░▓███▄░ ▓██    ▓██░     ░██   █▌▒███   ▓██  █▒░
▒██░    ▓▓█  ░██░▓██ █▄ ▒██    ▒██      ░▓█▄   ▌▒▓█  ▄  ▒██ █░░
░██████▒▒▒█████▓ ▒██▒ █▄▒██▒   ░██▒ ██▓ ░▒████▓ ░▒████▒  ▒▀█░  
░ ▒░▓  ░░▒▓▒ ▒ ▒ ▒ ▒▒ ▓▒░ ▒░   ░  ░ ▒▓▒  ▒▒▓  ▒ ░░ ▒░ ░  ░ ▐░  
░ ░ ▒  ░░░▒░ ░ ░ ░ ░▒ ▒░░  ░      ░ ░▒   ░ ▒  ▒  ░ ░  ░  ░ ░░  
  ░ ░    ░░░ ░ ░ ░ ░░ ░ ░      ░    ░    ░ ░  ░    ░       ░░  
    ░  ░   ░     ░  ░          ░     ░     ░       ░  ░     ░  
                                     ░   ░                 ░   

Notes on developing inside Distrobox

2025-03-13

I use a Fedora Atomic desktop, Bluefin. Because most of the operating system is read-only, I prefer to develop inside a Distrobox container. This allows me to install packages specifically for development within the Distrobox container.

How my setup works

I use Debian in Distrobox (called "Dev") on Fedora Atomic Bluefin, and install most of my packages through Homebrew. My "Dev" container shares the same home directory as the host system.

I mostly do everything out of this distrobox, including running certain commands on the host.

Tell GhosTTY to open a shell inside the box on start

First, because I can't be bothered entering the Distrobox manually each time, I have my terminal of choice setup to automatically SSH into it.

Here is the config line for GhosTTY:

# Config location: ~/.config/ghostty/config
command = /usr/bin/bash -c 'distrobox enter dev'

Now when I open GhosTTY, we know I mean business 😎

Execute certain commands on the host system

There is still a subset of things I need to do on the host system, like running updates, interacting with systemd, and installing flatpack packages to the host. I could open up a separate terminal for the host, but I am extremely lazy motivated by convenience.

The following Zsh function will give me an escape hatch function called host. If I run it without any arguments, it opens a shell in the host system (I can return at any time with exit). If I add arguments, it runs the command on the host and I get the stdout.

if [ -n "$DISTROBOX_ENTER_PATH" ]; then
  # Executes a command on the host
  host() {
    local host_zsh
    host_zsh=$(distrobox-host-exec command -v zsh)
    if [[ $# -eq 0 ]]; then
        distrobox-host-exec -- env NO_MOTD=true "$host_zsh"
    else
        distrobox-host-exec -- env NO_MOTD=true "$host_zsh" -c "$*"
    fi
    }
  # add aliases here (see below)
fi

Then, inside the condition block, I add aliases for whatever it makes sense to execute on the host; for example, anything system related.

alias ujust='host ujust'
alias open='host xdg-open'
alias code='host code'
alias flatpak='host flatpak'
alias rpm-ostree='host rpm-ostree'
alias bootc='host bootc'
alias journalctl='host journalctl'
alias systemctl='host systemctl'
alias htop='host htop'
alias uptime='host uptime'
alias distrobox-upgrade='host distrobox-upgrade'
alias distrobox='host distrobox'

Why do I have a condition block? Because I use the same dotfiles for the host and the distrobox container. Again, I'm lazy motivated by convenience.

Share Docker socket with the host system

To keep things simple, I install docker and docker-compose in the container and point them at the host's docker socket. This means they share access to the containers and volumes.

First, I have to install the docker-compose plugin and configure it:

# Alias the host docker socket
sudo ln -s /var/run/docker.sock /run/host/var/run/docker.sock
brew install docker docker-compose

Then I add docker-compose as a docker plugin by adding the following to ~/.config/docker/config.json:

{
  "cliPluginsExtraDirs": ["/home/linuxbrew/.linuxbrew/lib/docker/cli-plugins"]
}

Share 1Password SSH daemon with host

I use the host's 1Password package (installed using rpm-ostree) to provision my SSH keys. Using the below, I can seamlessly use the host's 1Password SSH daemon:

sudo mkdir -p /opt/1Password
sudo ln -s /run/host/opt/1Password/op-ssh-sign /opt/1Password/
git config --global gpg.ssh.program "/opt/1Password/op-ssh-sign"

Share clipboard with host

xsel is the only clipboard that I've found works within the container

if [ -n "$DISTROBOX_ENTER_PATH" ]; then
  brew install xsel
fi

Set Zsh as default shell

I like to set zsh as the default shell within the container

# Choose ZSH as default shell
command -v zsh | sudo tee -a /etc/shells # Add zsh to /etc/shells
sudo chsh -s "$(command -v zsh)" "${USER}" # Set zsh as default shell