Vaultwarden、Outline、Immichをセットアップ

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}}
タイトルとURLをコピーしました