目次
Vaultwarden
導入するサービスに応じて、ベースとなるコンテナをコピーし立ち上げます。vaultwardenの例で行います。Dockerでインストールする予定なので、lxd-base-dockerコンテナをコピーします。起動したあと一度再起動しておいたほうが良いです。
lxc exec vaultwarden -- bash
tailscale up --authkey=tskey-auth-xxxx
その後、Vaultwardenをインストールします。
#!/bin/bash
set -euo pipefail
INSTALL_DIR="/opt/vaultwarden"
# ── TailscaleのMagicDNS名を自動取得 ──────────────
TAILSCALE_DOMAIN=$(tailscale status --json | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(d['Self']['DNSName'].strip('.'))
")
if [ -z "$TAILSCALE_DOMAIN" ]; then
echo "ERROR: Tailscale MagicDNS名を取得できませんでした"
exit 1
fi
echo "🌐 ドメイン: https://${TAILSCALE_DOMAIN}"
# ── インストールディレクトリ ──────────────────────
mkdir -p "$INSTALL_DIR"
cd "$INSTALL_DIR"
# ── docker-compose.yml 生成 ──────────────────────
cat > docker-compose.yml <<EOF
services:
vaultwarden:
image: vaultwarden/server:latest
restart: unless-stopped
environment:
DOMAIN: https://${TAILSCALE_DOMAIN}
SIGNUPS_ALLOWED: "true"
volumes:
- ./data:/data
ports:
- "127.0.0.1:3000:80"
EOF
# ── 起動 ─────────────────────────────────────────
docker compose up -d
# ── Tailscale Serve 設定 ──────────────────────────
tailscale serve --bg http://127.0.0.1:3000
echo ""
echo "════════════════════════════════════════"
echo " ✅ Vaultwarden 起動完了"
echo "════════════════════════════════════════"
echo ""
echo " 🌐 アクセスURL : https://${TAILSCALE_DOMAIN}"
echo " 📂 インストール先: $INSTALL_DIR"
echo ""
echo " ⚠️ アカウント作成後はSIGNUPS_ALLOWEDをfalseに変更してください"
echo " sed -i 's/SIGNUPS_ALLOWED: \"true\"/SIGNUPS_ALLOWED: \"false\"/' $INSTALL_DIR/docker-compose.yml"
echo " docker compose -f $INSTALL_DIR/docker-compose.yml up -d"
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 ""
Outline
lxc exec outline -- bash
tailscale up --authkey=tskey-auth-xxxx
nano setup-outline.sh
# 下記スクリプトを貼り付け
bash setup-outline.sh
#!/bin/bash
set -euo pipefail
# =============================================================
# Outline セットアップスクリプト
#
# 構成:
# - Outline : https://outline.xxx.ts.net (443)
# - Dex : https://outline.xxx.ts.net:5556 (5556)
# - issuerとブラウザリダイレクトをDex URLに統一
# - コンテナ間通信はhttp://dex:5556で直接通信
# =============================================================
INSTALL_DIR="/opt/outline"
# ── TailscaleのMagicDNS名を自動取得 ──────────────
TAILSCALE_DOMAIN=$(tailscale status --json | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(d['Self']['DNSName'].strip('.'))
")
if [ -z "$TAILSCALE_DOMAIN" ]; then
echo "ERROR: Tailscale MagicDNS名を取得できませんでした"
exit 1
fi
BASE_URL="https://${TAILSCALE_DOMAIN}"
DEX_URL="https://${TAILSCALE_DOMAIN}:5556"
DEX_INTERNAL="http://dex:5556"
echo "🌐 Outline URL : $BASE_URL"
echo "🌐 Dex URL : $DEX_URL"
# ── ユーザー登録 ──────────────────────────────────
if ! command -v htpasswd &>/dev/null; then
apt-get install -y apache2-utils &>/dev/null
fi
USERS_YAML=""
USER_COUNT=0
while true; do
USER_COUNT=$((USER_COUNT + 1))
echo ""
echo "── ユーザー ${USER_COUNT} ──────────────────────"
read -rp "ユーザー名(例: yamada): " U_NAME
read -rsp "パスワード: " U_PASS
echo ""
U_HASH=$(htpasswd -bnBC 10 "" "${U_PASS}" | tr -d ':\n' | sed 's/\$2y/\$2a/')
U_UUID=$(cat /proc/sys/kernel/random/uuid)
U_EMAIL="${U_NAME}@local.invalid"
USERS_YAML="${USERS_YAML}
- email: \"${U_EMAIL}\"
hash: \"${U_HASH}\"
username: \"${U_NAME}\"
userID: \"${U_UUID}\""
read -rp "もう1人追加しますか? (y/N): " ADD_MORE
[[ "$ADD_MORE" =~ ^[Yy]$ ]] || break
done
# ── シークレットキー生成 ──────────────────────────
SECRET_KEY=$(openssl rand -hex 32)
UTILS_SECRET=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$(openssl rand -hex 16)
DEX_CLIENT_SECRET=$(openssl rand -hex 16)
# ── インストールディレクトリ ──────────────────────
mkdir -p "$INSTALL_DIR/dex/config"
mkdir -p "$INSTALL_DIR/data/storage"
chown -R 1001:1001 "$INSTALL_DIR/data/storage"
cd "$INSTALL_DIR"
# ── Dex設定ファイル生成 ───────────────────────────
cat > dex/config/config.yaml <<EOF
issuer: ${DEX_URL}
storage:
type: sqlite3
config:
file: /config/dex.db
web:
http: 0.0.0.0:5556
oauth2:
skipApprovalScreen: true
responseTypes:
- code
staticClients:
- id: outline
name: "Outline Wiki"
secret: "${DEX_CLIENT_SECRET}"
redirectURIs:
- "${BASE_URL}/auth/oidc.callback"
enablePasswordDB: true
staticPasswords:
${USERS_YAML}
logger:
level: info
format: text
EOF
chown -R 1001:1001 "$INSTALL_DIR/dex"
# ── .env 生成 ────────────────────────────────────
cat > .env <<EOF
SECRET_KEY=${SECRET_KEY}
UTILS_SECRET=${UTILS_SECRET}
URL=${BASE_URL}
PORT=3000
FORCE_HTTPS=false
DATABASE_URL=postgres://outline:${POSTGRES_PASSWORD}@postgres:5432/outline?sslmode=disable
REDIS_URL=redis://redis:6379
FILE_STORAGE=local
FILE_STORAGE_LOCAL_ROOT_DIR=/var/lib/outline/data
FILE_STORAGE_UPLOAD_MAX_SIZE=26214400
OIDC_CLIENT_ID=outline
OIDC_CLIENT_SECRET=${DEX_CLIENT_SECRET}
OIDC_AUTH_URI=${DEX_URL}/auth
OIDC_TOKEN_URI=${DEX_INTERNAL}/token
OIDC_USERINFO_URI=${DEX_INTERNAL}/userinfo
OIDC_DISPLAY_NAME=ログイン
OIDC_SCOPES=openid profile email offline_access
DEFAULT_LANGUAGE=ja_JP
LOG_LEVEL=info
EOF
chmod 600 .env
# ── docker-compose.yml 生成 ──────────────────────
cat > docker-compose.yml <<EOF
services:
outline:
image: outlinewiki/outline:latest
restart: unless-stopped
env_file: .env
ports:
- "127.0.0.1:3000:3000"
volumes:
- ./data/storage:/var/lib/outline/data
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
dex:
condition: service_healthy
dex:
image: ghcr.io/dexidp/dex:latest
restart: unless-stopped
command: dex serve /config/config.yaml
ports:
- "127.0.0.1:5556:5556"
volumes:
- ./dex/config:/config
healthcheck:
test: ["CMD-SHELL", "echo OK"]
interval: 10s
timeout: 5s
retries: 10
start_period: 10s
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: outline
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: outline
volumes:
- ./data/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U outline"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --save 60 1 --loglevel warning
volumes:
- ./data/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
EOF
# ── 起動 ─────────────────────────────────────────
docker compose up -d
# ── Tailscale Serve設定 ───────────────────────────
tailscale serve --bg http://localhost:3000
tailscale serve --bg --https=5556 http://localhost:5556
echo ""
echo "════════════════════════════════════════"
echo " ✅ Outline 起動完了"
echo "════════════════════════════════════════"
echo ""
echo " 🌐 アクセスURL : $BASE_URL"
echo " 🌐 Dex URL : $DEX_URL"
echo " 📂 インストール先: $INSTALL_DIR"
echo ""
echo " ▼ 登録済みユーザー:"
echo "$USERS_YAML" | grep 'email:' | sed 's/.*email: "\(.*\)"/ ログインID: \1/'
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 ""
Immich
写真データがかなりの容量になるので、データ部分用にサブボリュームを作成します。
STEP 1 ホスト側:@data-immichサブボリュームを作成
まずは@data-immichサブボリュームを作成します。
nano create-subvol-immich.sh
sudo bash create-subvol-immich.sh
#!/bin/bash
set -e
# デバイス自動検出(ルートパーティションのデバイスを取得)
BTRFS_DEV=$(findmnt -n -o SOURCE / | sed 's/\[.*//')
TMP_MOUNT="/mnt"
SUBVOL_NAME="@data-immich"
echo "デバイス: ${BTRFS_DEV}"
# トップレベルマウント
mount -o subvolid=5 "${BTRFS_DEV}" "${TMP_MOUNT}"
# サブボリューム作成
btrfs subvolume create "${TMP_MOUNT}/${SUBVOL_NAME}"
echo "[OK] ${SUBVOL_NAME} を作成しました"
# アンマウント
umount "${TMP_MOUNT}"
# 確認
echo ""
echo "作成済みサブボリューム一覧:"
mount -o subvolid=5 "${BTRFS_DEV}" "${TMP_MOUNT}"
btrfs subvolume list "${TMP_MOUNT}" | grep "@data-"
umount "${TMP_MOUNT}"
STEP 2 ホスト側:fstab登録 & マウント
#!/bin/bash
set -e
BTRFS_DEV=$(findmnt -n -o SOURCE / | sed 's/\[.*//')
SUBVOL_NAME="@data-immich"
MOUNT_POINT="/mnt/data/immich"
sudo mkdir -p "${MOUNT_POINT}"
if ! grep -q "${SUBVOL_NAME}" /etc/fstab; then
UUID=$(sudo blkid -s UUID -o value "${BTRFS_DEV}")
echo "UUID=${UUID} ${MOUNT_POINT} btrfs defaults,subvol=${SUBVOL_NAME},noatime 0 0" | sudo tee -a /etc/fstab
echo "[OK] /etc/fstab に追記"
fi
sudo mount "${MOUNT_POINT}"
echo "[OK] マウント完了"
findmnt "${MOUNT_POINT}"
STEP 3 ホスト側:LXDコンテナにディスク追加とパーミッション設定
LXD-UIでマウントします。ソースとパスはそれぞれ次を指定します。
/mnt/data/immich
/mnt/data/immich

追加したらパーミッション設定を行なっておきます。
lxc config set immich raw.idmap "both 1000 1000"
lxc restart immich
sudo chown 1000:1000 /mnt/data/immich
STEP 4 コンテナ内:Immich セットアップ&起動
まずはコンテナ内に入ります。
lxc exec immich -- bash
Tailscaleの認証。
tailscale up --authkey=tskey-auth-xxxx
スクリプトの作成と実行。
nano setup-immich.sh
# 下記スクリプトを貼り付け
bash setup-immich.sh
#!/bin/bash
set -e
WORK_DIR="/mnt/data/immich/docker"
mkdir -p "${WORK_DIR}"
cd "${WORK_DIR}"
# --- .env 生成(既存があればスキップ)---
if [ ! -f .env ]; then
DB_PASSWORD=$(openssl rand -hex 16)
cat > .env <<EOF
UPLOAD_LOCATION=./library
DB_DATA_LOCATION=./postgres
IMMICH_VERSION=release
DB_PASSWORD=${DB_PASSWORD}
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
EOF
chmod 600 .env
echo "[OK] .env を新規作成"
else
echo "[SKIP] .env は既存のものを使用"
DB_PASSWORD=$(grep DB_PASSWORD .env | cut -d= -f2)
fi
# --- docker-compose.yml 生成 ---
cat > docker-compose.yml <<'EOF'
name: immich
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- '127.0.0.1:2283:2283'
depends_on:
- redis
- database
restart: always
healthcheck:
disable: false
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
volumes:
- model-cache:/cache
env_file:
- .env
restart: always
healthcheck:
disable: false
redis:
container_name: immich_redis
image: docker.io/redis:6.2-alpine
healthcheck:
test: redis-cli ping || exit 1
restart: always
database:
container_name: immich_postgres
image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
healthcheck:
test: >
pg_isready --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' || exit 1;
Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}'
--tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0)
FROM pg_stat_database')";
echo "checksum failure count is $$Chksum";
[ "$$Chksum" = '0' ] || exit 1
interval: 5m
start_interval: 30s
start_period: 5m
restart: always
volumes:
model-cache:
EOF
# --- Tailscale Serve 設定(冪等) ---
tailscale serve --bg 2283
# --- フルドメイン名取得 ---
TAILSCALE_DOMAIN=$(tailscale status --json | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(d['Self']['DNSName'].rstrip('.'))
")
# --- 起動 ---
docker compose up -d
echo ""
echo "======================================"
echo " Immich 起動完了"
echo "======================================"
echo " URL : https://${TAILSCALE_DOMAIN}"
echo " DB_PASSWORD: ${DB_PASSWORD}"
echo " 設定ファイル: ${WORK_DIR}/.env"
echo "======================================"
テンプレート例COPY
{{y}}/{{y}}{{MM}}/{{y}}{{MM}}{{dd}}_{{album}}/{{filename}}


