brian@brianrogers.dev: ~/blog/mobile-homelab-camper
tmux 3.4 · zsh 5.9
~ ◉ articles ◎ mobile-homelab-camper K palette
◉ 2026-05-02 #homelab#mobile#networking ← back to articles

Mobile Homelab: Running Infrastructure in a Camper Van

A Raspberry Pi 5 running a stripped-down version of my home stack out of the camper — Starlink and cellular for connectivity, 460Ah of lithium for power, and Tailscale tying it back to the main homelab.

Why a Mobile Homelab?

The camper van gets used a lot. Most of it is weekend and week-long camping trips closer to home, plus a few longer cross-country runs each year. Either way, the van is the home that moves with me, which means anything I rely on at home that doesn't move with me effectively goes offline the moment I leave the driveway.

That was the original problem. I've spent years building out a homelab — Home Assistant, media, MQTT brokers, Prometheus and Grafana, the AI stack on homelab-ai — and it all stayed behind. The goal of this build was to bring just enough of it along to keep my workflow continuous on a long weekend out, and to wire the van directly into the home network so the two halves stay coherent instead of drifting apart.

The result is a Raspberry Pi 5 named "Orla" running a focused subset of the home stack, powered by the van's lithium bank, online via Starlink and cellular, and joined to my Tailscale network so the homelab back home sees it as just another node.

Connectivity: Starlink + Cellular

Connectivity is the load-bearing piece of the whole setup. Without it, the mobile homelab is islanded and Tailscale can't reach home. With it, the van behaves like a slightly slower spur of my home LAN.

The setup is simple in principle:

  • Starlink as the primary link. Roof-mounted dish, dedicated 12V converter, plenty of throughput for video calls, syncs, and software updates while parked.
  • Cellular modem as backup and failover. Active any time Starlink is obstructed, stowed, or unavailable — and the only practical option while actually driving down the highway.

The router handles failover so the LAN-side IP of the Pi never changes. From the Pi's perspective the upstream just disappears and reappears. Tailscale handles the rest: when connectivity comes back, the tunnel re-establishes within a few seconds and everything resumes.

The Hardware

The Raspberry Pi 5 is a real upgrade over previous generations. The 8GB model gives enough headroom to run multiple Docker containers without thrashing, and the I/O finally feels like it can keep up with an SSD.

The setup:

  • Raspberry Pi 5 Model B, 8GB RAM
  • Official active cooler — non-negotiable, the Pi 5 throttles fast under load without it
  • Samsung 500GB portable SSD for media and data
  • Argon ONE V3 case with M.2 expansion
  • USB-C PD power from the van's 12V system

Total power draw is around 5–8 watts at idle, spiking to maybe 12 watts under heavy load. Against a 460Ah lithium bank that's a rounding error — it can run 24/7 without any meaningful impact on the energy budget.

Power: 460Ah, Solar, and DC-DC

The van runs on a 460Ah lithium battery bank. There's no shore power dependency in the system — the bank is charged in two ways:

  • Solar panels on the roof, feeding a charge controller into the bank whenever there's daylight.
  • DC-DC alternator charger that pulls from the vehicle's alternator while driving, so every hour on the highway is also an hour of charging.

That combination is what makes the "park somewhere for a few days, then move, stay online the whole time" arrangement viable — and it's what makes the longer cross-country trips practical without rethinking the energy plan. Solar covers the parked days. The DC-DC charger covers the driving days. Between the two, the bank stays topped up without ever needing to plug into anything.

Home Assistant is the system of record for all of this — see the next section.

Tailscale: An Extension of the Home Network

The single most important piece of the mobile setup is Tailscale. It is what makes the van not an isolated island.

The Pi is on the same tailnet as homelab-ai and the rest of the home infrastructure. From inside the van I can SSH into homelab-ai as if I were sitting at my desk — kick off a fine-tuning run, query the local LLM, pull logs from Grafana, hit the management console. From home, the van's services are addressable on the same flat tailnet namespace. Nothing is exposed to the public internet; everything flows through WireGuard.

This shifts how I think about the mobile homelab entirely. It isn't a duplicate of the home stack — it's an extension of it. The van runs the things that need to be local (battery monitoring, media, MQTT for in-van sensors). Anything that needs more compute than a Pi can offer gets routed back to homelab-ai over Tailscale. The split happens along the lines of "what do I actually need on-board" rather than "what can I cram into the Pi."

The Software Stack

I deliberately kept this small. Every service has to earn its slot.

Home Assistant — Battery and Energy Tracking

This is the primary use case for Home Assistant in the van, not generic "smart home" stuff. HA tracks:

  • State of charge on the 460Ah bank, second by second.
  • Charge rates from the solar controller and the DC-DC charger separately, so I can see which source is doing the work at any given time.
  • Solar production across the day, surfaced as a Grafana-style chart on the HA dashboard.
  • Loads — fridge cycles, the Pi itself, lighting, the inverter when it's on.

That data is what tells me whether I can run the inverter for an hour, or whether I should drive instead of waiting on the sun. It also feeds Prometheus, which is where the long-term retention lives.

One quirk: Home Assistant needs network_mode: host to discover devices via mDNS, which matters for ESPHome sensors and other local integrations.

services:
  homeassistant:
    image: ghcr.io/home-assistant/home-assistant:stable
    container_name: homeassistant
    network_mode: host
    volumes:
      - ./homeassistant:/config
      - /etc/localtime:/etc/localtime:ro
    restart: unless-stopped

Jellyfin

A local media server means movies and shows work without internet — which matters because Starlink in motion isn't always reliable, and cellular bandwidth isn't infinite. I sync a rotating selection of content to the SSD before long stretches. Jellyfin transcodes acceptably on the Pi 5 but I direct-play whenever possible to save the CPU.

Mosquitto MQTT

The MQTT broker is the glue for the in-van IoT layer. Temperature sensors, door sensors, the various ESP32 boards I've added over time — they all publish to Mosquitto, and Home Assistant subscribes to whatever it cares about.

Prometheus + Grafana

Even on a weekend trip I want real visibility. Prometheus scrapes the Pi (via node_exporter), the Docker containers, and Home Assistant itself. Grafana renders the dashboards I actually look at: battery SOC trends, solar production by day, in-van temperature, container health.

This sounds like overkill for a van. It isn't. When you're parked somewhere remote and a service drops, having actual time-series data beats guessing at what changed.

The Docker Compose

Everything runs in Docker, managed by a single compose file:

services:
  homeassistant:
    image: ghcr.io/home-assistant/home-assistant:stable
    network_mode: host
    volumes:
      - ./homeassistant:/config
    restart: unless-stopped

  jellyfin:
    image: jellyfin/jellyfin
    ports:
      - "8096:8096"
    volumes:
      - ./jellyfin/config:/config
      - ./jellyfin/cache:/cache
      - /mnt/media:/media:ro
    restart: unless-stopped

  mosquitto:
    image: eclipse-mosquitto
    ports:
      - "1883:1883"
    volumes:
      - ./mosquitto:/mosquitto/config
    restart: unless-stopped

  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus:/etc/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.retention.time=30d'
    restart: unless-stopped

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    volumes:
      - ./grafana:/var/lib/grafana
    restart: unless-stopped

30-day retention on Prometheus is enough for spotting trends — if I want longer history, the same metrics get scraped from homelab-ai over Tailscale and persisted in the home Prometheus, which has plenty of disk.

Ansible for Configuration

The mobile stack is managed by the same Ansible setup as the home infrastructure. Getting that to work cleanly took some doing — Raspberry Pi OS has different package names and paths than Ubuntu, and I'd built the original roles assuming Ubuntu.

The key is conditional logic on ansible_distribution:

- name: Install common packages
  apt:
    name:
      - git
      - curl
      - htop
    state: present

- name: Install Ubuntu-specific packages
  apt:
    name:
      - software-properties-common
    state: present
  when: ansible_distribution == "Ubuntu"

Some packages like ntp don't exist on Raspberry Pi OS (it uses systemd-timesyncd instead), and the Docker repository sources need to use debian rather than ubuntu. Once those edge cases are handled, the same playbooks provision the van and the home machines, which is the entire point of using Ansible in the first place.

Networking Notes

Past the Starlink/cellular failover above, a few small details matter:

Local DNS: dnsmasq on the Pi resolves *.van.local names so I can hit services by hostname regardless of which upstream is currently active.

Tailscale subnet routing: the Pi advertises the van's LAN subnet to the tailnet, which means devices in the van that aren't themselves on Tailscale (the Vue tablet on the dash, for instance) are still reachable from home.

Offline-first behavior: everything important keeps running without internet. Home Assistant automations execute locally, Jellyfin serves local media, Prometheus keeps scraping. When the upstream comes back, anything that needs to sync (config backups, metrics push to the home Prometheus) catches up.

Power-Aware Behaviors

The Pi's draw is small enough that it doesn't really need to throttle, but I still wired some power-aware automations into Home Assistant so the system degrades gracefully if the bank ever does get low:

  • Below 50% SOC, non-essential services (Grafana, Prometheus) shut down
  • Below 30%, everything except Home Assistant stops
  • The Pi itself stays on unless we hit critical levels — at that point bigger problems need solving than which containers are running

In practice these thresholds basically never trigger because of the solar + DC-DC combo. But the rules exist, and the dashboard tells me when they would.

Lessons Learned

ARM compatibility matters. Not every Docker image has ARM builds. I had to find alternatives or build my own for a couple of services. Always check for linux/arm64 support before committing to a tool.

SD cards die. I learned this the hard way. Boot from SD if you have to, but keep all real data on the SSD. Better yet, use the Argon case with M.2 and boot from NVMe entirely.

Heat is real. The Pi 5 thermal-throttles in a hot van without active cooling. The official cooler is worth it. I also added a 12V fan in the cabinet that kicks on when ambient exceeds 30°C.

Sync before you leave. Nothing worse than realizing three hours into a drive that you forgot to update the local media library or push a config change. I have a pre-trip checklist that includes media sync, config backup, and a quick Tailscale connectivity check.

What's Next

The current setup works well, but there are a few additions I want to wire in:

OBD-II → Prometheus + Grafana

The biggest planned addition is OBD-II integration. I want the van itself to be a first-class telemetry source on the same monitoring stack that already covers the homelab.

The plan:

  • An OBD-II adapter (Bluetooth or WiFi, depending on which one I land on) plugged into the diagnostic port.
  • A small reader process on the Pi that polls the adapter for engine RPM, coolant and intake temps, fuel economy / instantaneous fuel rate, throttle position, and the assorted DTC / status fields.
  • Those values exposed as a Prometheus exporter on the Pi, scraped on the same interval as everything else.
  • A new Grafana dashboard pinning the van's vitals next to the homelab's — engine temp and battery SOC on the same screen.

The interesting part is that because the Pi is already on Tailscale, the home Prometheus can also scrape the van directly. I'd get long-term retention of every trip's engine data — weekend camping runs and the bigger cross-country drives alike — on the same disk that holds my homelab metrics. Fuel economy by route, coolant trends across seasons, RPM histograms — all queryable from one Grafana.

Other Ideas

  • Local voice assistant using Wyoming/Piper — "Hey van, turn on the lights"
  • GPS tracking and trip logging, fed into the same time-series database as the OBD data
  • Signal mapping: log Starlink obstruction events and cellular signal strength along the route, so I can build a map of "good places to stop and work"

The goal isn't to replicate the full homelab in the van — that's what Tailscale is for. It's to have just enough on-board infrastructure to stay comfortable on a weekend out, keep the van itself observable, and let the rest of the homelab feel like it's still one network away whether the trip is two nights or two weeks. The Pi 5 hits that sweet spot of capable enough to be useful while small enough to fit in a drawer.

READING ⎇ main ⚡ 0 errors ◐ tokyo-night ssh · 127.0.0.1 00:00:00