remote-docker-nas

📁 htlin222/dotfiles 📅 5 days ago
3
总安装量
3
周安装量
#58643
全站排名
安装命令
npx skills add https://github.com/htlin222/dotfiles --skill remote-docker-nas

Agent 安装分布

claude-code 3
mcpjam 2
kilo 2
junie 2
windsurf 2
zencoder 2

Skill 文档

Remote Docker on NAS

Overview

Run Docker on a remote NAS/server controlled from your laptop via SSH — no local Docker daemon needed. Uses crane (~11MB) to pull images as tarballs and DOCKER_HOST=ssh:// to send commands to the remote daemon.

Architecture

Laptop (control plane)                NAS/Server (execution)
──────────────────────                ────────────────────────
docker-compose.yml (local)            Docker daemon
Makefile (local)              ──SSH──▶ Containers run here
source code (local)                   Data stored here
crane pull → .tar (local)             docker load → images here

Prerequisites

Laptop:

  • crane — lightweight image puller, no daemon needed
    brew install crane
    
  • docker CLI only (no Docker Desktop / OrbStack needed)
  • SSH access to NAS (~/.ssh/config configured)

NAS/Server:

  • Docker daemon running
  • Your user in the docker group
    sudo usermod -aG docker <nas-user>
    # Log out and back in for group to take effect
    # Verify: groups | grep docker
    

Setup

Set DOCKER_HOST to route all docker commands via SSH:

export DOCKER_HOST=ssh://<nas-host>

# Verify
docker info

Add to shell profile for persistence:

# ~/.zshrc or ~/.bashrc
export DOCKER_HOST=ssh://<nas-host>

Image Workflow

When the NAS cannot reach Docker Hub (firewall/network), pull images on the laptop with crane and load them onto the NAS:

# 1. Pull image as tarball (runs on laptop, no daemon needed)
crane pull nginx:alpine /tmp/nginx-alpine.tar

# 2. Load onto NAS (DOCKER_HOST routes this via SSH)
docker load -i /tmp/nginx-alpine.tar

# 3. Run
docker run -d --name web -p 8080:80 nginx:alpine

Key Gotchas

Aspect Behavior
Compose file Read from laptop (local path)
Volume paths Mounted from NAS filesystem (must be absolute NAS paths)
Source files Must be copied to NAS via scp before compose up
Port binding Ports bind on NAS IP, access via http://<nas-ip>:<port>

The #1 mistake: Using relative volume paths like ./html:/usr/share/nginx/html. This resolves on the NAS, not your laptop. Use absolute NAS paths.

Docker Compose Template

services:
  web:
    image: nginx:alpine
    ports:
      - "8888:80"
    volumes:
      - /home/<nas-user>/project/html:/usr/share/nginx/html:ro
    restart: unless-stopped

Deploy workflow:

# 1. Copy source files to NAS
scp -r ./html <nas-host>:/home/<nas-user>/project/

# 2. Pull and load images
crane pull nginx:alpine /tmp/nginx-alpine.tar
docker load -i /tmp/nginx-alpine.tar

# 3. Start (compose file is local, execution is remote)
docker compose up -d

Makefile Template

DOCKER_HOST := ssh://<nas-host>
NAS_PROJECT := /home/<nas-user>/project
export DOCKER_HOST

IMAGES := nginx:alpine

.PHONY: pull load sync up down ps logs clean

pull:
	@for img in $(IMAGES); do \
		echo "Pulling $$img..."; \
		crane pull $$img /tmp/$$(echo $$img | tr '/:' '-').tar; \
	done

load:
	@for img in $(IMAGES); do \
		tarfile=/tmp/$$(echo $$img | tr '/:' '-').tar; \
		echo "Loading $$tarfile..."; \
		docker load -i $$tarfile; \
	done

sync:
	scp -r ./html <nas-host>:$(NAS_PROJECT)/

up: pull load sync
	docker compose up -d

down:
	docker compose down

ps:
	docker compose ps

logs:
	docker compose logs -f

clean: down
	docker compose rm -f
	docker image prune -f

Reference Stacks

Ready-to-deploy compose files in references/. Replace <nas-host> and <nas-user> with your values.

Stack File Port Description
Nginx Proxy Manager nginx-reverse-proxy.yml 80, 443, 81 Reverse proxy with SSL termination and web UI
FreshRSS freshrss.yml 8280 RSS feed aggregator with PostgreSQL backend
BookLore booklore.yml 6060 Digital library for EPUB/PDF/CBZ with auto-metadata
Telegram Bot telegram-bot.yml — Python bot running 24/7, custom Dockerfile

Quick deploy any stack:

# Example: deploy FreshRSS
crane pull freshrss/freshrss:latest /tmp/freshrss.tar
crane pull postgres:16-alpine /tmp/postgres-16-alpine.tar
docker load -i /tmp/freshrss.tar
docker load -i /tmp/postgres-16-alpine.tar
docker compose -f references/freshrss.yml up -d

Multi-service architecture (recommended for production):

Nginx Proxy Manager (:80/:443)
  ├── FreshRSS    → localhost:8280
  ├── BookLore    → localhost:6060
  └── Other apps  → localhost:XXXX

Troubleshooting

Problem Cause Fix
permission denied on docker socket User not in docker group sudo usermod -aG docker <user> + re-login
Group added but still denied Session not refreshed Log out and back in, or newgrp docker
connection reset by peer on pull NAS can’t reach Docker Hub Use crane pull + docker load workflow
403 Forbidden on volume Files don’t exist on NAS scp files to NAS first
port already in use Another service on that port Change port mapping or stop conflicting service
sudo needs password via SSH No TTY allocated Use ssh -t for interactive, or add user to docker group