広く公開するサービスではなく、個人用途で利用するだけなら、外部からのアクセスをTailscale前提で構築するほうが便利ですね。なら、むしろ推奨される構成です。Tailscaleのネットワーク内にいる端末しかアクセスできないので、インターネットには一切公開されません。MagicDNSとHTTPS証明書はTailscaleが自動管理するので、自己署名証明書の煩わしさもありません。
規模により2つのアプローチ
Tailscale前提にするにしても、作成するコンテナ数に応じて、2つのアプローチが考えられます。
A) 各コンテナにTailscale(シンプル)
[端末] → Tailscale → [immichコンテナ:Tailscale] https://immich.tail.ts.net
[nextcloudコンテナ:Tailscale] https://nextcloud.tail.ts.net
- 各コンテナが独立したTailscaleノードになる
- コンテナごとにTailscaleの認証が必要
- ノード数が増える(無料プランは最大100台)
B) ホストにTailscale + リバースプロキシ(スマート)
[端末] → Tailscale → [ホスト:Tailscale+Caddy] → immichコンテナ
→ nextcloudコンテナ
- Tailscaleはホスト1台分だけ
- Caddyが
https://immich→コンテナへ自動振り分け - 証明書管理も一元化
Caddyは設定がシンプルで、Tailscale連携も公式でサポートされているため、個人用途でコンテナ数が増える予定ならこの構成が適しています。次のようなイメージです。
[端末] ↓ Tailscale [Ubuntuホスト] ├── Tailscale(ホストのみ) ├── Caddyコンテナ(リバースプロキシ) │ ↓ LXD内部ネットワーク(lxdbr0) ├── immichコンテナ ├── nextcloudコンテナ └── ...
それぞれの特徴
100以上のサービスを利用する予定ならCaddy一元化が良いでしょうが、個人用途でコンテナが10個以下なら、各コンテナにTailscaleのプランAのほうが却ってトラブルが少ないかもしれません。
| Caddy一元化 | 各コンテナにTailscale | |
|---|---|---|
| 管理 | シンプル | コンテナ増えると煩雑 |
| 認証系トラブル | 設定次第で解決可能 | ほぼ発生しない |
| Tailscaleノード数 | 1 | サービス数分 |
| 個人100台制限 | 余裕 | 気にしなくていい |
ここでは認証系のトラブルが発生しにくい、シンプルな構成で進めていきます。
[端末]
↓ Tailscale
[Ubuntuホスト] ← Tailscale済み
│ https://host.tail.ts.net:8443 → LXD UI
│
├── immichコンテナ
│ └── Tailscale → https://immich.tail.ts.net
├── nextcloudコンテナ
│ └── Tailscale → https://nextcloud.tail.ts.net
└── ...
ベースコンテナを作成
Dockerが不要な最小構成コンテナと、Dockerが必要なコンテナをベースコンテナとして準備します。
lxd-base-minimal:nano、Tailscale
lxd-base-docker:nano + Tailscale + Docker
lxd-base-minimalコンテナ作成
LXD-UIで作成し、コンテナ内に入って作業します。

lxc exec lxd-base-minimal -- bash
apt update && apt upgrade -y
apt install -y nano curl
# Tailscaleインストール
curl -fsSL https://tailscale.com/install.sh | sh
# ここでスナップショット → コピー
# tailscale up --authkey=... は各コンテナで実行
lxd-base-dockerコンテナ作成
lxd-base-minimalコンテナをコピーして作業します。
こちらはNestingをAllowにしてから起動します。

lxc exec lxd-base-docker -- bash
# Dockerインストール
curl -fsSL https://get.docker.com | sh
TailscaleのAuth Key発行
各コンテナでTailscaleの認証が必要になるので、使い捨てのAuth Keyを発行しておきます。
Auth Keyの取得・使用手順
- Tailscale管理画面にログイン: Admin console にアクセスします。
- Settings > Keysへ移動: サイドバーの「Settings」から「Keys」を選択します。
- キーを生成: 「Generate auth key…」をクリックしてキーを作成します。
- 設定の選択: 必要に応じて、期限なし(Reusable)やタグ付け(Tags)などのオプションを設定し、キーをコピーして保存します。

Linkwardenセットアップ
導入するサービスに応じて、ベースとなるコンテナをコピーし立ち上げます。linkwardenの例で行います。Dockerでインストールする予定なので、lxd-base-dockerコンテナをコピーします。
lxc exec linkwarden -- bash
tailscale up --authkey=tskey-auth-xxxx
tailscale serve --bg http://localhost:3000
その後、Linkwardenをインストールします。
#!/bin/bash
set -euo pipefail
INSTALL_DIR="/opt/linkwarden"
# ── TailscaleのMagicDNS名を自動取得 ──────────────
TAILSCALE_URL=$(tailscale status --json | python3 -c "
import json, sys
data = json.load(sys.stdin)
dns = data.get('MagicDNSSuffix', '')
name = data.get('Self', {}).get('HostName', '')
print(f'https://{name}.{dns}' if dns and name else 'http://localhost:3000')
")
echo "🌐 NEXTAUTH_URL: $TAILSCALE_URL"
# ── シークレットキー自動生成 ──────────────────────
NEXTAUTH_SECRET=$(openssl rand -base64 36)
POSTGRES_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32)
# ── インストールディレクトリ ──────────────────────
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR"
# ── .env 生成 ────────────────────────────────────
cat > .env <<EOF
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
NEXTAUTH_URL=${TAILSCALE_URL}
EOF
chmod 600 .env
# ── docker-compose.yml 生成 ──────────────────────
cat > docker-compose.yml <<'COMPOSE'
services:
postgres:
image: postgres:16-alpine
restart: unless-stopped
env_file: .env
environment:
POSTGRES_DB: linkwarden
POSTGRES_USER: linkwarden
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U linkwarden -d linkwarden"]
interval: 10s
timeout: 5s
retries: 5
linkwarden:
image: ghcr.io/linkwarden/linkwarden:latest
restart: unless-stopped
env_file: .env
environment:
DATABASE_URL: postgresql://linkwarden:${POSTGRES_PASSWORD}@postgres:5432/linkwarden
ports:
- "3000:3000"
volumes:
- linkwarden_data:/data/data
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
linkwarden_data:
COMPOSE
# ── 起動 ─────────────────────────────────────────
docker compose pull
docker compose up -d
echo ""
echo "════════════════════════════════════════"
echo " ✅ Linkwarden 起動完了"
echo "════════════════════════════════════════"
echo ""
echo " 🌐 アクセスURL : $TAILSCALE_URL"
echo " 📂 インストール先: $INSTALL_DIR"
echo ""
echo " ログ確認 : docker compose -f $INSTALL_DIR/docker-compose.yml logs -f"
echo " 停止 : docker compose -f $INSTALL_DIR/docker-compose.yml down"
echo " 更新 : docker compose -f $INSTALL_DIR/docker-compose.yml pull && docker compose -f $INSTALL_DIR/docker-compose.yml up -d"
echo ""

