Local-Path PVC: SSH Sidecar for Developer Access¶
Apps on local-path storage are fast but inaccessible from the network — unlike NFS, there is no share to mount. This pattern adds a minimal SSH sidecar to the pod that shares the same PVC, enabling live bidirectional file sync from a developer machine via Mutagen.
graph LR
MAC["Mac\n~/mnt/ha-config"]
MUTAGEN["Mutagen\n(FSEvents + SFTP)"]
PF["kubectl port-forward\n:2222 → pod:22"]
SSH["SSH sidecar\nalpine + openssh"]
PVC["local-path PVC\n/config"]
APP["App container\n/config"]
MAC <-->|"live sync\ntwo-way-safe"| MUTAGEN
MUTAGEN <-->|"SSH"| PF
PF <-->|"port-forward"| SSH
SSH <--> PVC
APP <--> PVC
Development use only
This sidecar is a convenience tool for editing config files. Do not use it as a backup mechanism — use the SQLite + local-path pattern for durability.
Kubernetes Resources¶
SSH public key ConfigMap¶
apiVersion: v1
kind: ConfigMap
metadata:
name: ssh-authorized-keys
namespace: <ns>
data:
authorized_keys: |
ssh-ed25519 AAAA... user@host
Public keys in git are fine
SSH public keys are designed to be shared. Only the private key (on your Mac) is sensitive. A ConfigMap is appropriate here — no need for ExternalSecrets.
Sidecar container¶
Add to the app Deployment alongside the existing container:
- name: ssh
image: alpine:3.21
command: ["/bin/sh", "-c"]
args:
- |
apk add --no-cache openssh-server &&
mkdir -p /root/.ssh &&
cp /ssh-keys/authorized_keys /root/.ssh/authorized_keys &&
chmod 700 /root/.ssh && chmod 600 /root/.ssh/authorized_keys &&
ssh-keygen -A &&
exec /usr/sbin/sshd -D -e \
-o PermitRootLogin=yes \
-o PasswordAuthentication=no \
-o AllowTcpForwarding=no \
-o X11Forwarding=no
ports:
- containerPort: 22
volumeMounts:
- name: <config-volume>
mountPath: /config
- name: ssh-keys
mountPath: /ssh-keys
readOnly: true
Add the ConfigMap volume to the pod's volumes list:
The existing config volume (<config-volume>) is reused — both containers share the same PVC.
Developer Machine Setup¶
Prerequisites (one-time)¶
No macFUSE or kernel extensions required.
SSH config (one-time, added automatically by the script below)¶
Host ha-sftp-local
HostName localhost
Port 2222
User root
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
StrictHostKeyChecking=no is safe here — the sidecar generates fresh SSH host keys on every pod restart, and the connection is local-only via kubectl port-forward.
Sync script¶
#!/usr/bin/env bash
set -euo pipefail
LOCAL_DIR="$HOME/mnt/ha-config"
PF_PID_FILE="/tmp/ha-mount-pf.pid"
NAMESPACE="home"
DEPLOYMENT="homeassistant"
LOCAL_PORT=2222
SSH_KEY="$HOME/.ssh/id_ed25519"
MUTAGEN_SESSION="ha-config"
SSH_ALIAS="ha-sftp-local"
ensure_ssh_config() {
if ! grep -q "Host ${SSH_ALIAS}" ~/.ssh/config 2>/dev/null; then
cat >> ~/.ssh/config <<EOF
Host ${SSH_ALIAS}
HostName localhost
Port ${LOCAL_PORT}
User root
IdentityFile ${SSH_KEY}
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
EOF
fi
}
start() {
mkdir -p "$LOCAL_DIR"
ensure_ssh_config
lsof -ti "tcp:${LOCAL_PORT}" | xargs kill -9 2>/dev/null || true
sleep 1
kubectl port-forward -n "$NAMESPACE" "deploy/$DEPLOYMENT" "$LOCAL_PORT:22" &
echo $! > "$PF_PID_FILE"
sleep 2
mutagen sync create \
--name "$MUTAGEN_SESSION" \
--sync-mode two-way-safe \
--ignore "home-assistant_v2.db*" \
--ignore "*.log" --ignore "*.log.*" \
--ignore ".storage" \
"$LOCAL_DIR" "${SSH_ALIAS}:/config"
echo "Live sync: $LOCAL_DIR ↔ pod:/config"
}
stop() {
mutagen sync terminate "$MUTAGEN_SESSION" 2>/dev/null || true
[ -f "$PF_PID_FILE" ] && kill "$(cat "$PF_PID_FILE")" 2>/dev/null || true
rm -f "$PF_PID_FILE"
}
status() { mutagen sync list "$MUTAGEN_SESSION"; }
case "${1:-start}" in
start) start ;; stop) stop ;; status) status ;;
*) echo "Usage: $0 [start|stop|status]" ; exit 1 ;;
esac
Usage¶
ha-mount.sh start # start port-forward + Mutagen session
# edit files in ~/mnt/ha-config — syncs both ways instantly
ha-mount.sh status # check sync health / conflicts
ha-mount.sh stop # terminate
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
Permission denied (publickey) |
SSH config has wrong User |
Run sed -i '' 's/User sftp/User root/' ~/.ssh/config |
server magic number incorrect |
SFTP-only container (e.g. atmoz/sftp) blocks Mutagen agent |
Use full openssh sidecar — atmoz/sftp forces internal-sftp |
rclone mount fails on Apple Silicon |
Homebrew rclone strips macFUSE; official binary needs Recovery Mode kext approval | Use Mutagen instead — no kernel extension needed |
| Port 2222 already in use | Stale port-forward from previous session | lsof -ti tcp:2222 \| xargs kill -9 |
| Mutagen conflict | Both HA and local edited the same file | Run mutagen sync list ha-config to identify, resolve manually |