Turning a 2013 Dell E6540 into a Dedicated TV Media Controller
The Setup ## The Journey (aka What Went Wrong) ### 1. Ubuntu 24.04's Layered Squashfs ### 2. Finding gnome-initial-setup ### 3. The "Welcome to Ubuntu" Imposter ### 4. Auto-Login Configuration ### 5. Chrome Crashes on Live USB ### 6. pactl: command not found ### 7. Slow USB Write on macOS ### 8. EFI Partition Extraction ## The Final Solution ## Use It Yourself ## Key Takeaways ## Hardware Recommendation TL;DR: I spent 12+ hours fighting Ubuntu 24.04's layered squashfs, hidden welcome wizards, and Chrome sandbox crashes to create a turnkey live USB for my old Dell E6540 laptop. Here's everything I learned so you don't have to. I have a Dell Latitude E6540 (circa 2013) collecting dust. I also have a TV that needs a dedicated browser for streaming. The plan: create a custom Ubuntu live USB that boots directly into Chrome, no setup wizards, no login screens, just plug and play. The Problem: Ubuntu 24.04 doesn't use a single filesystem.squashfs anymore. It uses a layered system: What I Tried: Merging all layers into one filesystem.squashfs. What Happened: Boot failure - "File system layers are missing" The Fix: Keep ALL layers intact. Only modify the specific layer containing what you need to change. Casper expects the layered structure. Reference: Ubuntu Casper Manual The Problem: The GNOME initial setup wizard kept appearing on boot. I needed to remove it. What I Tried: Searching and removing from the top layer only. What Happened: Still appeared. The binary wasn't in the top layer. The Fix: Search ALL layers using unsquashfs -l (list mode - fast!) instead of extracting: Lesson: gnome-initial-setup lives in minimal.squashfs (the base layer). The Problem: After removing gnome-initial-setup, a "Welcome to Ubuntu" wizard STILL appeared. What I Tried: Removing more gnome-initial-setup files, masking systemd services, creating done files. What Happened: Still appeared. Different icon, different behavior. The Fix: It's NOT gnome-initial-setup. It's ubuntu-desktop-bootstrap - a completely different snap package! Lesson: Don't assume. Check the actual desktop shortcut or process name. The Problem: System landed on login screen instead of auto-logging in. The Fix: Configure GDM properly in the squashfs: The Problem: Chrome installed fine but crashed immediately on launch. Error reporter appeared. What I Tried: Adding --disable-gpu (wrong assumption about nomodeset). The Real Problem: Chrome's sandbox doesn't play nice with overlayfs (used by live USB environments). Note: --no-sandbox reduces security but is necessary for live USB environments. The Problem: Script failed with pactl: command not found on Ubuntu 24.04 live environment. Why: Ubuntu 24.04 uses PipeWire, and pactl (PulseAudio control) may not be available in the minimal live environment. The Fix: Check before using: The Problem: Writing 6GB ISO took 1085 seconds (~18 minutes). The Fix: Use raw device /dev/rdisk instead of /dev/disk: Why: /dev/rdisk bypasses macOS's buffer cache, giving ~10x faster writes. The Problem: Custom ISO wouldn't boot on UEFI systems. The Fix: Extract and preserve the original EFI partition: I built a Docker-based tool that: If you have a Dell E6540 (or similar older laptop) and want a dedicated TV/media controller: GitHub: github.com/tv6540/cubic2 Boot the USB, and the setup script runs automatically to configure display, install Chrome, and you're ready to stream. The Logitech K400 Plus is perfect for this setup: Built with mass frustration, mass trial-and-error, and mass caffeine. Templates let you quickly answer FAQs or store snippets for re-use. as well , this person and/or COMMAND_BLOCK:
casper/
├── minimal.squashfs # Base layer
├── minimal.standard.squashfs # Standard additions
└── minimal.standard.live.squashfs # Live environment customizations COMMAND_BLOCK:
casper/
├── minimal.squashfs # Base layer
├── minimal.standard.squashfs # Standard additions
└── minimal.standard.live.squashfs # Live environment customizations COMMAND_BLOCK:
casper/
├── minimal.squashfs # Base layer
├── minimal.standard.squashfs # Standard additions
└── minimal.standard.live.squashfs # Live environment customizations CODE_BLOCK:
for layer in minimal minimal.standard minimal.standard.live; do FOUND=$(unsquashfs -l "$CASPER_DIR/${layer}.squashfs" 2>/dev/null | \ grep -E "gnome-initial-setup" || true) if [ -n "$FOUND" ]; then echo "Found in ${layer}.squashfs!" fi
done CODE_BLOCK:
for layer in minimal minimal.standard minimal.standard.live; do FOUND=$(unsquashfs -l "$CASPER_DIR/${layer}.squashfs" 2>/dev/null | \ grep -E "gnome-initial-setup" || true) if [ -n "$FOUND" ]; then echo "Found in ${layer}.squashfs!" fi
done CODE_BLOCK:
for layer in minimal minimal.standard minimal.standard.live; do FOUND=$(unsquashfs -l "$CASPER_DIR/${layer}.squashfs" 2>/dev/null | \ grep -E "gnome-initial-setup" || true) if [ -n "$FOUND" ]; then echo "Found in ${layer}.squashfs!" fi
done COMMAND_BLOCK:
# Search for the REAL culprit
unsquashfs -l layer.squashfs | grep -E "ubuntu-desktop-bootstrap|desktop-bootstrap" COMMAND_BLOCK:
# Search for the REAL culprit
unsquashfs -l layer.squashfs | grep -E "ubuntu-desktop-bootstrap|desktop-bootstrap" COMMAND_BLOCK:
# Search for the REAL culprit
unsquashfs -l layer.squashfs | grep -E "ubuntu-desktop-bootstrap|desktop-bootstrap" COMMAND_BLOCK:
mkdir -p "$SQUASH_DIR/etc/gdm3"
cat > "$SQUASH_DIR/etc/gdm3/custom.conf" << 'EOF'
[daemon]
InitialSetupEnable=false
AutomaticLoginEnable=true
AutomaticLogin=ubuntu
EOF COMMAND_BLOCK:
mkdir -p "$SQUASH_DIR/etc/gdm3"
cat > "$SQUASH_DIR/etc/gdm3/custom.conf" << 'EOF'
[daemon]
InitialSetupEnable=false
AutomaticLoginEnable=true
AutomaticLogin=ubuntu
EOF COMMAND_BLOCK:
mkdir -p "$SQUASH_DIR/etc/gdm3"
cat > "$SQUASH_DIR/etc/gdm3/custom.conf" << 'EOF'
[daemon]
InitialSetupEnable=false
AutomaticLoginEnable=true
AutomaticLogin=ubuntu
EOF CODE_BLOCK:
google-chrome-stable \ --no-first-run \ --no-default-browser-check \ --disable-session-crashed-bubble \ --no-sandbox \ "https://your-url-here" CODE_BLOCK:
google-chrome-stable \ --no-first-run \ --no-default-browser-check \ --disable-session-crashed-bubble \ --no-sandbox \ "https://your-url-here" CODE_BLOCK:
google-chrome-stable \ --no-first-run \ --no-default-browser-check \ --disable-session-crashed-bubble \ --no-sandbox \ "https://your-url-here" CODE_BLOCK:
if command -v pactl &>/dev/null; then HDMI_SINK=$(pactl list short sinks | grep -i hdmi | head -1 | awk '{print $2}') [ -n "$HDMI_SINK" ] && pactl set-default-sink "$HDMI_SINK"
else echo "Warning: pactl not available"
fi CODE_BLOCK:
if command -v pactl &>/dev/null; then HDMI_SINK=$(pactl list short sinks | grep -i hdmi | head -1 | awk '{print $2}') [ -n "$HDMI_SINK" ] && pactl set-default-sink "$HDMI_SINK"
else echo "Warning: pactl not available"
fi CODE_BLOCK:
if command -v pactl &>/dev/null; then HDMI_SINK=$(pactl list short sinks | grep -i hdmi | head -1 | awk '{print $2}') [ -n "$HDMI_SINK" ] && pactl set-default-sink "$HDMI_SINK"
else echo "Warning: pactl not available"
fi COMMAND_BLOCK:
# Slow (~18 min for 6GB)
sudo dd if=ubuntu.iso of=/dev/disk4 bs=4m # Fast (~2 min for 6GB) - use rdisk
RAW_DEVICE=$(echo "/dev/disk4" | sed 's|/dev/disk|/dev/rdisk|')
sudo dd if=ubuntu.iso of=$RAW_DEVICE bs=4m status=progress COMMAND_BLOCK:
# Slow (~18 min for 6GB)
sudo dd if=ubuntu.iso of=/dev/disk4 bs=4m # Fast (~2 min for 6GB) - use rdisk
RAW_DEVICE=$(echo "/dev/disk4" | sed 's|/dev/disk|/dev/rdisk|')
sudo dd if=ubuntu.iso of=$RAW_DEVICE bs=4m status=progress COMMAND_BLOCK:
# Slow (~18 min for 6GB)
sudo dd if=ubuntu.iso of=/dev/disk4 bs=4m # Fast (~2 min for 6GB) - use rdisk
RAW_DEVICE=$(echo "/dev/disk4" | sed 's|/dev/disk|/dev/rdisk|')
sudo dd if=ubuntu.iso of=$RAW_DEVICE bs=4m status=progress COMMAND_BLOCK:
# Get EFI partition info from original ISO
EFI_INFO=$(xorriso -indev "$ISO_IN" -report_el_torito as_mkisofs 2>&1 | \ grep -A1 "append_partition 2")
INTERVAL=$(echo "$EFI_INFO" | grep -oP '\d+d-\d+d' | head -1) # Extract it
START_SECTOR=$(echo "$INTERVAL" | cut -d'-' -f1 | tr -d 'd')
END_SECTOR=$(echo "$INTERVAL" | cut -d'-' -f2 | tr -d 'd')
COUNT=$((END_SECTOR - START_SECTOR + 1))
dd if="$ISO_IN" of="$EFI_IMG" bs=512 skip="$START_SECTOR" count="$COUNT" COMMAND_BLOCK:
# Get EFI partition info from original ISO
EFI_INFO=$(xorriso -indev "$ISO_IN" -report_el_torito as_mkisofs 2>&1 | \ grep -A1 "append_partition 2")
INTERVAL=$(echo "$EFI_INFO" | grep -oP '\d+d-\d+d' | head -1) # Extract it
START_SECTOR=$(echo "$INTERVAL" | cut -d'-' -f1 | tr -d 'd')
END_SECTOR=$(echo "$INTERVAL" | cut -d'-' -f2 | tr -d 'd')
COUNT=$((END_SECTOR - START_SECTOR + 1))
dd if="$ISO_IN" of="$EFI_IMG" bs=512 skip="$START_SECTOR" count="$COUNT" COMMAND_BLOCK:
# Get EFI partition info from original ISO
EFI_INFO=$(xorriso -indev "$ISO_IN" -report_el_torito as_mkisofs 2>&1 | \ grep -A1 "append_partition 2")
INTERVAL=$(echo "$EFI_INFO" | grep -oP '\d+d-\d+d' | head -1) # Extract it
START_SECTOR=$(echo "$INTERVAL" | cut -d'-' -f1 | tr -d 'd')
END_SECTOR=$(echo "$INTERVAL" | cut -d'-' -f2 | tr -d 'd')
COUNT=$((END_SECTOR - START_SECTOR + 1))
dd if="$ISO_IN" of="$EFI_IMG" bs=512 skip="$START_SECTOR" count="$COUNT" COMMAND_BLOCK:
./make.sh # Does everything: clean, build, write to USB COMMAND_BLOCK:
./make.sh # Does everything: clean, build, write to USB COMMAND_BLOCK:
./make.sh # Does everything: clean, build, write to USB COMMAND_BLOCK:
git clone https://github.com/tv6540/cubic2
cd cubic2
./make.sh COMMAND_BLOCK:
git clone https://github.com/tv6540/cubic2
cd cubic2
./make.sh COMMAND_BLOCK:
git clone https://github.com/tv6540/cubic2
cd cubic2
./make.sh - Dell Latitude E6540 (i7, 8GB RAM)
- Logitech K400 Plus Wireless Keyboard - perfect for couch browsing
- Any USB drive (8GB+) - Downloads Ubuntu 24.04 desktop ISO
- Extracts and modifies the layered squashfs
- Removes ALL welcome wizards (gnome-initial-setup, gnome-tour, ubuntu-desktop-bootstrap)
- Configures auto-login
- Pre-installs wallpapers, Chrome policies, dark mode
- Rebuilds a bootable hybrid ISO (BIOS + UEFI)
- Writes to USB with one command - macOS or Linux
- USB drive (8GB+)
- Logitech K400 Plus (recommended for couch use) - Prompt for sudo (kept alive during build)
- Show USB device picker
- Confirm before erasing
- Download Ubuntu ISO (cached for future builds)
- Build custom ISO in Docker
- Write to USB - Ubuntu 24.04 uses layered squashfs - don't merge them, modify in place
- unsquashfs -l is your friend - list files without extracting (fast!)
- ubuntu-desktop-bootstrap != gnome-initial-setup - check process names
- Chrome needs --no-sandbox on live USB - overlayfs breaks the sandbox
- Use /dev/rdisk on macOS - 10x faster USB writes
- Get all prompts upfront - nobody wants to wait 20 min then type "yes" - Wireless with tiny USB receiver
- Built-in trackpad
- Long battery life
- Compact for couch use