Contents

dev-containers-16-09-2026

Yesterday I failed to write something to my blog. But I wanted to add that I started working with dev containers. And I kind of like it. I still have to learn more about it. Or rather just use it a bit more. For my use case I’m using it for my Angular project. So in this case I have a Angular json file as well. Yesterday I failed to write something to my blog. The way it works I see is that you have a directory which inside is also lets say where you would put your out put in. So it looks something like this:

…/Projects/test ❯ tree -a -L 2
.
├── .devcontainer
│   ├── devcontainer.json
│   └── setup-tools.sh
└── my-app
    ├── .angular
    ├── angular.json
    ├── dist
    ├── .editorconfig
    ├── .gitignore
    ├── node_modules
    ├── package.json
    ├── package-lock.json
    ├── .prettierrc
    ├── public
    ├── README.md
    ├── src
    ├── tsconfig.app.json
    ├── tsconfig.json
    ├── tsconfig.spec.json
    └── .vscode

Inside the .devcontainer direectory there I place the json thats needed. Beside that is there a setup-tools script that is getting all the extra tools I like to use when it comes to developing. I’m not quite sure yet if I want to keep it like this or rather get something like this directly from my dotfiles.

The nice thing about it is I don’t have to fysically install anything on my own machine. But it will rather stay inside a container which is using docker engine. I could also have went with Podman. But this seems a bit different then I’m use too and will be maybe done at a later stage.

About devcontainer.json: The linux environement comes with zsh which I never use. I’m not sure yet whether it comes with the image provided. I have to have an extra look at this still.

Below are all the files that we helpfull to set things up.

setup script for install devpod

# Installs the host prerequisites for using DevPod with a Docker backend.
# Agnostic: no language/framework-specific config is generated here.
set -e

ORANGE='\033[38;5;214m'
NC='\033[0m'
GREEN='\e[1;32m'

resolve_devpod() {
  if command -v devpod >/dev/null 2>&1; then
    echo "$(command -v devpod)"
  else
    echo "$HOME/.local/bin/devpod"
  fi
}

create_welcome_message() {
  clear
  echo -e "${ORANGE}══════════════════════════════════════════════════${NC}"
  echo -e "${ORANGE}   DEVPOD / DEV CONTAINER PREREQUISITES${NC}"
  echo -e "${ORANGE}══════════════════════════════════════════════════${NC}\n"
}

setup_docker() {
  if ! groups | grep -qw docker; then
    echo "→ Adding user to docker group..."
    sudo usermod -aG docker "$USER"
    echo -e "${GREEN}✓ group added — re-login needed before Docker works:\n    newgrp docker   (or log out/in)${NC}"
  fi
  if ! systemctl is-active --quiet docker; then
    echo "→ Starting & enabling Docker daemon..."
    sudo systemctl enable --now docker
  fi
  echo -e "${GREEN}✓ Docker ready${NC}"
}

install_devpod() {
  DEVPOD_BIN="$(resolve_devpod)"
  if [[ -x "$DEVPOD_BIN" ]]; then
    echo -e "${GREEN}✓ DevPod already installed: $("$DEVPOD_BIN" --version)${NC}"
    return
  fi
  mkdir -p "$HOME/.local/bin"
  echo "→ Downloading DevPod CLI..."
  curl -fsSL -o "$HOME/.local/bin/devpod" \
    https://github.com/loft-sh/devpod/releases/latest/download/devpod-linux-amd64
  chmod +x "$HOME/.local/bin/devpod"
  echo -e "${GREEN}✓ DevPod installed: $("$HOME/.local/bin/devpod" --version)${NC}"
}

configure_devpod() {
  DEVPOD_BIN="$(resolve_devpod)"
  "$DEVPOD_BIN" provider add docker >/dev/null 2>&1 || true
  "$DEVPOD_BIN" ide use none >/dev/null 2>&1 || true
  echo -e "${GREEN}✓ DevPod configured (docker provider, no IDE)${NC}"
}

print_next_steps() {
  echo -e "\n${GREEN}Next steps:${NC}"
  if ! groups | grep -qw docker; then
    echo "  1. newgrp docker               # activate docker group in this shell"
  fi
  echo "  1. cd <project>                 # project with a .devcontainer/ or Dockerfile"
  echo "  2. devpod up .                  # build & start the workspace"
  echo "  3. devpod ssh <name>.devpod     # shell into the container"
}

create_welcome_message
setup_docker
install_devpod
configure_devpod
print_next_steps

devcontainer.json

{
  "name": "Angular Dev",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:22",
  "containerEnv": {
    "XDG_CONFIG_HOME": "/home/node/.config"
  },
  "features": {
    "ghcr.io/devcontainers/features/common-utils:2": {},
    "ghcr.io/stu-bell/devcontainer-features/neovim:0": {}
  },
  "mounts": [
    "source=${localEnv:HOME}/.config/nvim,target=/home/node/.config/nvim,type=bind",
    "source=${localEnv:HOME}/.config/tmux,target=/home/node/.config/tmux,type=bind"
  ],
  "forwardPorts": [4200],
  "portsAttributes": { "4200": { "label": "Angular Dev Server" } },
  "postCreateCommand": "sudo bash .devcontainer/setup-tools.sh && sudo npm install -g npm@latest @angular/cli opencode-ai"
}

setup-tools.sh

# Installs terminal/CLI tools LazyVim plugins expect: fzf, fd, ripgrep, bat, tmux, lazygit.
set -euo pipefail

apt-get update -y >/dev/null
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
  fzf fd-find bat ripgrep tmux >/dev/null

# Debian ships fd as `fdfind` and bat as `batcat` — provide the expected names.
[ -e /usr/bin/fdfind ] && ln -sf /usr/bin/fdfind /usr/local/bin/fd
[ -e /usr/bin/batcat ] && ln -sf /usr/bin/batcat /usr/local/bin/bat

# lazygit (not in Debian repos) — static binary from the latest release.
# Resolve the version tag via the /releases/latest redirect, avoiding GitHub API rate limits.
if ! command -v lazygit >/dev/null 2>&1; then
  tag="$(curl -fsSI https://github.com/jesseduffield/lazygit/releases/latest \
    | grep -i '^location:' | tr -d '\r' | sed -E 's#.*/tag/(v[0-9][^ /]*) *$#\1#')"
  if [[ -n "$tag" ]]; then
    curl -fsSL "https://github.com/jesseduffield/lazygit/releases/download/$tag/lazygit_${tag#v}_Linux_x86_64.tar.gz" \
      | tar -xz -C /tmp lazygit
    install -m755 /tmp/lazygit /usr/local/bin/lazygit
  else
    echo "lazygit download skipped (could not resolve latest tag)"
  fi
fi

echo "Installed: fzf fd rg bat tmux lazygit"