SD Card Dashboard is a localhost-first web UI for backing up Raspberry Pi OS or any other SD cards, shrinking the captured image automatically with PiShrink, and providing ability to flash one or more SD cards in parallel from a clean dashboard with live progress tracking.
It is designed for SDcard backup and flash imaging workflows where a workstation needs direct access to removable USB card reader drives, accurate device visibility, and a simple operator-friendly interface.
This dashboard runs in Docker, serves a browser UI on http://localhost:8099, and manages a host-side SD card workflow with a Python backend.
Core workflow:
- Detect removable USB drives or SD card readers and show them in the dashboard.
- Capture a backup image from a selected source SD card using
dd. - Automatically run
pishrink.shon the captured image. - Save the resulting image in the configured backup directory.
- Write selected images back to one or more detected target cards.
- Show live progress, timing, and system log while operations run.
- Create backup images directly from SD cards.
- Automatically shrink captured images with PiShrink.
- Save images to a configurable backup directory.
- Default backup directory support through Docker environment variables.
- Detect removable USB drives or SDcard readers and show device paths such as
/dev/sdb,/dev/sdc, and/dev/sdd. - Show mount points and partition information for each detected card.
- Protect the active destination drive and system-mounted disks from destructive operations.
- Write the same image or different images to multiple selected target cards.
- Use
ddas the write backend with live progress parsing. - Show live activity cards with progress bars, elapsed time, per-target details, and a shared system log.
- Clear and reformat cards to FAT32 with sequential
SDcardNlabels. - Eject individual cards or all removable cards from the dashboard.
- Run as a Dockerized localhost service with an optional
systemdboot unit.
- Backend: Python HTTP server
- Frontend: HTML, CSS, and vanilla JavaScript
- Runtime: Docker Compose
- Imaging backend:
dd - Image shrinking: PiShrink (
pishrink.sh) - Device discovery: host block-device inspection via
/dev,/sys, andudev
Key files:
- Backend:
app/server.py - Frontend HTML:
app/static/index.html - Frontend styles:
app/static/styles.css - Frontend logic:
app/static/app.js - Docker definition:
Dockerfile - Compose stack:
docker-compose.yml - Boot service:
sdcard-dashboard-compose.service
This project performs real disk operations.
- Always double-check the selected target devices before writing.
- Never loosen the protected-drive logic unless you are certain what will become writable.
- Use the dashboard only when you are comfortable identifying removable media on your workstation.
- This container runs
privilegedbecause it must access host block devices and related system interfaces.
The containerized dashboard depends on a few host-side tools and paths:
- Docker Engine
- Docker Compose plugin
pishrink.shinstalled on the host at/usr/local/bin/pishrink.shudisks2and D-Bus if you want eject actions to work reliably- A real backup destination path, or an updated
DEFAULT_DESTINATION_DIR
These commands prepare a fresh Ubuntu system for this dashboard.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now dockerOptional:
sudo usermod -aG docker "$USER"Log out and back in after adding yourself to the docker group.
sudo apt update
sudo apt install -y \
dbus \
dosfstools \
e2fsprogs \
gzip \
parted \
pigz \
udev \
udisks2 \
wget \
xz-utilswget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
chmod +x pishrink.sh
sudo install -m 0755 pishrink.sh /usr/local/bin/pishrink.sh
rm -f pishrink.shUpdate this to match your real storage device if needed:
sudo mkdir -p /media/$USER/T9/pi-goldenAfter installing all prerequisites, clone the repository, place it in the expected folder path, review the default backup destination, and then launch the dashboard.
mkdir -p /home/username/Desktop
git clone https://github.com/saad-git-007/sd-card-dashboard.git /home/username/Desktop/SD_dashboard_foldercd /home/username/Desktop/SD_dashboard_folderOpen docker-compose.yml and update DEFAULT_DESTINATION_DIR if your backup drive or mount path is different from the example used in this repository.
sudo docker compose up --build -dsudo docker compose ps
curl http://localhost:8099/healthzhttp://localhost:8099
sudo install -m 0644 sdcard-dashboard-compose.service /etc/systemd/system/sdcard-dashboard-compose.service
sudo systemctl daemon-reload
sudo systemctl enable --now sdcard-dashboard-compose.service
sudo systemctl status sdcard-dashboard-compose.serviceThe default runtime settings live in docker-compose.yml:
- Host port:
8099 - App port inside container:
8080 - Default backup directory:
/media/user-name/T9/pi-golden - PiShrink path in container:
/usr/local/bin/pishrink.sh
If your backup drive or path is different, update:
DEFAULT_DESTINATION_DIRindocker-compose.yml
Start:
sudo docker compose up -dRebuild and start:
sudo docker compose up --build -dShow logs:
sudo docker compose logs -fStop and remove the stack:
sudo docker compose downThis repository includes a boot unit file: sdcard-dashboard-compose.service
Install and enable it:
cd /home/username/Desktop/SD_dashboard_folder
sudo install -m 0644 sdcard-dashboard-compose.service /etc/systemd/system/sdcard-dashboard-compose.service
sudo systemctl daemon-reload
sudo systemctl enable --now sdcard-dashboard-compose.serviceCheck status:
sudo systemctl status sdcard-dashboard-compose.serviceRestart the boot-managed compose stack:
sudo systemctl restart sdcard-dashboard-compose.serviceDisable automatic boot startup:
sudo systemctl disable --now sdcard-dashboard-compose.serviceHow it works:
ExecStartrunsdocker compose up -d --remove-orphansExecReloadreruns the same commandExecStoprunsdocker compose down
This is more reliable than relying only on Docker container restart policy because it recreates the compose project and network if needed.
Try:
sudo systemctl restart docker
sudo systemctl restart sdcard-dashboard-compose.serviceThen check:
sudo systemctl status docker sdcard-dashboard-compose.service --no-pager
cd /home/username/Desktop/SD_dashboard_folder
sudo docker compose ps
curl http://localhost:8099/healthzOn some Ubuntu systems, Docker can appear active after boot while its API socket is still unusable. If docker info fails even though docker.service shows active, the more reliable fix is to stop relying on socket activation with -H fd:// and make Docker bind /run/docker.sock directly.
Check the current state:
sudo systemctl status docker containerd --no-pager
sudo docker info
sudo systemctl cat docker.serviceMake sure containerd is enabled:
sudo systemctl enable containerd.serviceThen replace the Docker host unit with a direct Unix-socket version:
sudo tee /etc/systemd/system/docker.service > /dev/null <<'EOF'
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target nss-lookup.target firewalld.service containerd.service time-set.target
Wants=network-online.target containerd.service
StartLimitBurst=3
StartLimitIntervalSec=60
[Service]
Type=notify
ExecStart=/usr/bin/dockerd -H unix:///run/docker.sock --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
RestartSec=2
Restart=always
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
OOMScoreAdjust=-500
[Install]
WantedBy=multi-user.target
EOFDisable the old socket-activation path and restart Docker:
sudo systemctl disable --now docker.socket
sudo systemctl daemon-reload
sudo systemctl restart dockerThen restart the dashboard boot service:
sudo systemctl restart sdcard-dashboard-compose.service
curl http://localhost:8099/healthzThis repository's sdcard-dashboard-compose.service is set up to depend on docker.service and includes an ExecStartPre wait loop that waits for docker info to succeed before starting the dashboard.
Change the published port in docker-compose.yml, for example:
ports:
- "8100:8080"Then rebuild:
sudo docker compose up --build -dCheck that the host file exists:
ls -l /usr/local/bin/pishrink.shCheck that udisks2 and D-Bus are available on the host:
sudo systemctl status udisks2 --no-pagerThis project is suitable for local development, imaging stations, and internal deployment on Ubuntu-based hosts that need direct access to removable SD cards.
