add box script to create LXC containers for dev
arnaucube git@arnaucube.com
Sat, 28 Feb 2026 22:50:46 +0100
2 files changed,
183 insertions(+),
0 deletions(-)
A
bin/box
@@ -0,0 +1,137 @@
+#!/usr/bin/env bash +# +# Launch LXC containers to work in there. + +# Needs: sudo snap install lxd +# initialize lxd: sudo lxd init --auto + +# NOTE: in case the container does not have internet: +# 1) +# sudo sysctl -w net.ipv4.ip_forward=1 +# +# 2) +# # Force iptables to ACCEPT traffic from the LXD bridge +# # Docker sets the global policy to DROP; we must explicitly allow lxdbr0 +# sudo iptables -I FORWARD -i lxdbr0 -j ACCEPT +# sudo iptables -I FORWARD -o lxdbr0 -j ACCEPT +# +# 3) +# # allow LXD bridge through firewall: +# sudo ufw allow in on lxdbr0 +# sudo ufw route allow in on lxdbr0 +# sudo ufw route allow out on lxdbr0 +# +# Note: when turning off and on the host machine, might need to set the iptables +# again: step 3). + + +set -e + +if [ -z "$1" ]; then + echo "Error: no container name provided." + echo "Usage: ./box.sh <container_name>" + exit 1 +fi + +CONTAINER_NAME="$1" +HOST_SHARE_DIR="/home/$USER/container_data/$CONTAINER_NAME" +CONTAINER_SHARE_DIR="/mnt/shared" +IMAGE="ubuntu:22.04" + +PROFILE_NAME=${2:-"default"} # if $2 is empty, use "default" +PROFILES_FILE="$HOME/bin/box_profiles.sh" + +# EXPOSE_PORTS="7000 7001 7002 7003 7004 7005 7006 7007 7008 7009 7010" +EXPOSE_PORTS="" + + +if [ -z "$CONTAINER_NAME" ]; then + echo "error: no container name provided." + echo "Usage: ./box.sh <container_name> [profile_name]" + exit 1 +fi + +if [ ! -f "$PROFILES_FILE" ]; then + echo "error: $PROFILES_FILE not found." + exit 1 +fi + +source "$PROFILES_FILE" + +if ! declare -f "profile_$PROFILE_NAME" > /dev/null; then + echo "error: profile '$PROFILE_NAME' not found in $PROFILES_FILE." + echo "Available profiles are:" + grep -oP '(?<=profile_).*(?=\(\))' "$PROFILES_FILE" + exit 1 +fi + +echo "profile selected: [$PROFILE_NAME]" +echo "software to install: $SOFTWARE_LIST" + +mkdir -p "$HOST_SHARE_DIR" + +echo "launch LXD container: $CONTAINER_NAME..." +# security.nesting=true allows Docker to run inside this container +lxc launch "$IMAGE" "$CONTAINER_NAME" -c security.nesting=true + +sleep 2 # wait for filesystem + +echo "set dns" +HOST_UID=$(id -u) +# map Host User to Container Root +lxc config set "$CONTAINER_NAME" raw.idmap "both $HOST_UID 0" +# set dns to bypass host dns +lxc exec "$CONTAINER_NAME" -- rm -f /etc/resolv.conf +lxc exec "$CONTAINER_NAME" -- bash -c "echo 'nameserver 8.8.8.8' | tee /etc/resolv.conf" + +echo "restart container, sleep 2s" +lxc restart "$CONTAINER_NAME" +sleep 2 + +echo "configure shared directory (between container & host)" +lxc config device add "$CONTAINER_NAME" project_storage disk \ + source="$HOST_SHARE_DIR" \ + path="$CONTAINER_SHARE_DIR" + +echo "configure port forwarding" +for PORT in $EXPOSE_PORTS; do + lxc config device add "$CONTAINER_NAME" "proxy${PORT}" proxy \ + listen=tcp:0.0.0.0:${PORT} \ + connect=tcp:127.0.0.1:${PORT} +done + +echo "check internet" +if ! lxc exec "$CONTAINER_NAME" -- ping -c 1 8.8.8.8 > /dev/null; then + echo "error: no internet access inside the container." + echo "note: check out the 'NOTE' at the top of the script for instructions." + # remove container, since failed the launch + lxc stop $CONTAINER_NAME + lxc delete $CONTAINER_NAME + exit 1 +fi + +echo "apt update & apt install" +lxc exec "$CONTAINER_NAME" -- bash -c " + $(cat "$PROFILES_FILE") + + export DEBIAN_FRONTEND=noninteractive + export NEEDRESTART_MODE=a # 'a' for automatic restart + + apt-get update && apt-get upgrade -y + + if declare -f profile_$PROFILE_NAME > /dev/null; then + profile_$PROFILE_NAME + else + echo 'error: profile function $PROFILE_NAME not found.' + exit 1 + fi +" + +echo "setup complete" +echo "----------------------------------------------------" +echo "Shared Directory (Host): $HOST_SHARE_DIR" +echo "Shared Directory (Container): $CONTAINER_SHARE_DIR" +echo "----------------------------------------------------" +echo "To enter the container, run: lxc shell $CONTAINER_NAME" +echo "To stop the container, run: lxc stop $CONTAINER_NAME" +echo "To remove the container, run: lxc delete $CONTAINER_NAME"
A
bin/box_profiles.sh
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash +# +# See `box` file for more details. + +profile_minimal() { + apt-get install -y neovim git curl wget +} + +profile_rust() { + apt-get install -y neovim git curl build-essential + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +} + +profile_nodejs() { + apt-get install -y curl + curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - + apt-get install -y nodejs +} + +profile_docker() { + apt-get install -y ca-certificates curl gnupg + install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg + + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ + https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \ + > /etc/apt/sources.list.d/docker.list + + apt-get update + apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin +} + +profile_dev_env() { + git clone https://github.com/arnaucube/configs.git # includes rust + cd configs + yes | bash install-new-server.sh +} + +profile_default() { + profile_minimal + profile_nodejs + profile_docker + profile_ai + profile_dev_env # includes rust +}