いったんこのあたりで整理。Ubuntu 25.10 Serverに一からセットアップを想定しています。
Dockerでインストールするファイルは/opt/docker以下に各フォルダを作成しています。
Tailscale
# アップデート
sudo apt update
sudo apt upgrade -y
# Tailscale
sudo apt install curl
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
# SSH
sudo apt install -y openssh-server
以降はSSHで。
Cockpit、Samba、LXD-UI、Docker
# ================================================================
# Cockpit
# ================================================================
sudo apt install -y nano cockpit
# Ubuntu 25.10で管理者権限変更できるように
sudo update-alternatives --set sudo /usr/bin/sudo.ws
# 仮想マシンプラグイン
sudo apt install -y cockpit-machines
# ファイラー Navigator プラグイン
sudo apt install -y git
git clone https://github.com/45Drives/cockpit-navigator.git
cd cockpit-navigator
git checkout v0.5.8
sudo apt install -y make
sudo make install
cd ..
rm -rf cockpit-navigator
# Samba を Cockpit で管理
sudo apt install -y samba
wget https://github.com/45Drives/cockpit-file-sharing/releases/download/v4.5.3-4/cockpit-file-sharing_4.5.3-4jammy_all.deb
sudo apt install -y ./cockpit-file-sharing_4.5.3-4jammy_all.deb
rm cockpit-file-sharing_4.5.3-4jammy_all.deb
# ================================================================
# LXD-UI
# ================================================================
# LXD インストール
sudo snap install lxd --channel=latest/stable
# 初期化
sudo lxd init --minimal
# HTTPS API を有効化
sudo lxc config set core.https_address :8443
# UI 有効化
sudo snap set lxd ui.enable=true
sudo systemctl reload snap.lxd.daemon
# ユーザーを lxd グループに追加
sudo usermod -aG lxd $USER
# ================================================================
# Docker
# ================================================================
# パスを通す(Docker インストール前に適用が必要なため先に実行)
echo 'export PATH=$PATH:/snap/bin' >> ~/.bashrc
export PATH=$PATH:/snap/bin
# 必要パッケージのインストール
sudo apt update
sudo apt install -y ca-certificates curl gnupg
# GPG キー
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# apt リポジトリの追加(Ubuntu 25.x は noble を明示指定)
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
noble stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
# Docker Engine や関連ツールのインストール
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Docker 関連用ディレクトリを作成
sudo mkdir -p /opt/docker
# 所有者を「自分」に、グループを「docker」に変更
sudo chown -R $USER:docker /opt/docker
# 権限を設定(自分とグループが読み書きできるようにする)
sudo chmod -R 775 /opt/docker
# SGID を設定(新しく作る子ディレクトリのグループを強制的に「docker」にする)
sudo chmod -R g+s /opt/docker
# 一般ユーザーへ権限追加
sudo usermod -aG docker $USER
echo "一度ログアウト・ログイン、または「sudo reboot」で再起動してください"
ここで再起動します。
sudo reboot
Cockpit https://ホスト名:9090
LXD-UI https://ホスト名:8443
Outline
sudo mkdir -p /opt/docker/outline
cd /opt/docker/outline
sudo nano setup-outline.sh
# 下記スクリプトを貼り付け
sudo bash setup-outline.sh
#!/bin/bash
# =============================================================
# Outline セルフホスト 完全セットアップスクリプト
# 対象OS: Ubuntu 25.10 / インストール先: /opt/docker/outline
#
# 実施内容:
# 1. Outline インストール(メール認証 / Mailhog)
# 2. バックアップスクリプト作成・配置
# 3. リストアスクリプト作成・配置
# 4. cron 未インストールなら自動インストール
# 5. 毎日 2:00 AM バックアップを cron に登録
#
# - backups/ ディレクトリの権限を確実に一般ユーザー所有に設定
# - スクリプトに実行権限を付与
# - cron 登録を一般ユーザーのcrontabに登録
# - リストア時の chown を sudo で実行
# =============================================================
# sudo で実行されている場合は実行ユーザーを特定
if [ -n "$SUDO_USER" ]; then
REAL_USER="$SUDO_USER"
REAL_HOME=$(eval echo "~$SUDO_USER")
else
REAL_USER="$USER"
REAL_HOME="$HOME"
fi
INSTALL_DIR="/opt/docker/outline"
BACKUP_DIR="$INSTALL_DIR/backups"
echo "================================================"
echo " Outline 完全セットアップ"
echo " インストール先: $INSTALL_DIR"
echo " 実行ユーザー : $REAL_USER"
echo "================================================"
echo ""
# ── ホスト名 / IP を入力 ─────────────────────────────────────
DETECTED=$(hostname -I | awk '{print $1}')
echo "アクセスURLを設定します。"
echo "例: 192.168.1.100 / hostname / outline.example.com"
echo "(そのままEnterで検出されたIP: ${DETECTED} を使用)"
echo ""
read -rp "ホスト名またはIPアドレス: " INPUT_HOST
HOST="${INPUT_HOST:-$DETECTED}"
BASE_URL="http://${HOST}:3000"
echo ""
echo "▼ アクセスURL: ${BASE_URL}"
echo ""
# ================================================================
# STEP 1: 既存環境をクリーンアップ
# ================================================================
echo "------------------------------------------------"
echo " STEP 1: 既存環境をクリーンアップ"
echo "------------------------------------------------"
if [ -f "$INSTALL_DIR/docker-compose.yml" ]; then
docker compose -f "$INSTALL_DIR/docker-compose.yml" down -v 2>/dev/null || true
fi
rm -rf "$INSTALL_DIR"
echo " クリーンアップ完了"
# ================================================================
# STEP 2: ディレクトリ作成・権限設定
# ================================================================
echo "------------------------------------------------"
echo " STEP 2: ディレクトリ作成・権限設定"
echo "------------------------------------------------"
mkdir -p "$INSTALL_DIR"/{data/postgres,data/redis,data/storage}
mkdir -p "$BACKUP_DIR"
# data/storage は Outline コンテナ内ユーザー(UID 1001)が書き込めるよう設定
# (設定しないとエクスポート時に permission denied が発生する)
chown -R 1001:1001 "$INSTALL_DIR/data/storage"
chown -R "$REAL_USER":"$REAL_USER" "$INSTALL_DIR/data/postgres"
chown -R "$REAL_USER":"$REAL_USER" "$INSTALL_DIR/data/redis"
chown -R "$REAL_USER":"$REAL_USER" "$BACKUP_DIR"
# ACL で管理ユーザーが sudo なしで操作可能に
if ! command -v setfacl &>/dev/null; then
apt-get install -y acl &>/dev/null
fi
setfacl -R -m u:"$REAL_USER":rwx "$INSTALL_DIR"
setfacl -d -m u:"$REAL_USER":rwx "$INSTALL_DIR"
echo " ディレクトリ・権限設定完了"
cd "$INSTALL_DIR"
# ================================================================
# STEP 3: シークレットキー生成
# ================================================================
echo "------------------------------------------------"
echo " STEP 3: シークレットキー生成"
echo "------------------------------------------------"
SECRET_KEY=$(openssl rand -hex 32)
UTILS_SECRET=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$(openssl rand -hex 16)
echo " SECRET_KEY : $SECRET_KEY"
echo " UTILS_SECRET : $UTILS_SECRET"
echo " POSTGRES_PASSWORD: $POSTGRES_PASSWORD"
# ================================================================
# STEP 4: .env 作成
# ================================================================
echo "------------------------------------------------"
echo " STEP 4: .env 作成"
echo "------------------------------------------------"
cat > "$INSTALL_DIR/.env" <<EOF
# ============================================================
# Outline 環境変数
# 生成日時: $(date '+%Y-%m-%d %H:%M:%S')
# ============================================================
SECRET_KEY=${SECRET_KEY}
UTILS_SECRET=${UTILS_SECRET}
# アクセスURL
URL=${BASE_URL}
PORT=3000
# HTTPS強制を無効(HTTP運用時に必須)
FORCE_HTTPS=false
# DB接続(?sslmode=disable でローカルPostgresのSSLエラーを回避)
DATABASE_URL=postgres://outline:${POSTGRES_PASSWORD}@postgres:5432/outline?sslmode=disable
# Redis
REDIS_URL=redis://redis:6379
# ファイルストレージ(ローカル保存)
FILE_STORAGE=local
FILE_STORAGE_LOCAL_ROOT_DIR=/var/lib/outline/data
FILE_STORAGE_UPLOAD_MAX_SIZE=26214400
# SMTP(Mailhog ローカルSMTP)
SMTP_HOST=mailhog
SMTP_PORT=1025
SMTP_FROM_EMAIL=outline@example.com
SMTP_REPLY_EMAIL=outline@example.com
SMTP_SECURE=false
# 言語
DEFAULT_LANGUAGE=ja_JP
# ログ
LOG_LEVEL=info
# --- 外部認証プロバイダー(必要に応じてコメントを外す) ---
# [Slack]
# SLACK_CLIENT_ID=
# SLACK_CLIENT_SECRET=
#
# [Google]
# GOOGLE_CLIENT_ID=
# GOOGLE_CLIENT_SECRET=
EOF
echo " .env 作成完了"
# ================================================================
# STEP 5: docker-compose.yml 作成
# ================================================================
echo "------------------------------------------------"
echo " STEP 5: docker-compose.yml 作成"
echo "------------------------------------------------"
# POSTGRES_PASSWORD は値を直接埋め込む(env_file経由では environment: に展開されないため)
cat > "$INSTALL_DIR/docker-compose.yml" <<EOF
services:
outline:
image: outlinewiki/outline:latest
container_name: outline
restart: unless-stopped
env_file: .env
ports:
- "3000:3000"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
mailhog:
condition: service_started
volumes:
- ./data/storage:/var/lib/outline/data
networks:
- outline-net
postgres:
image: postgres:16-alpine
container_name: outline-postgres
restart: unless-stopped
environment:
POSTGRES_USER: outline
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: outline
volumes:
- ./data/postgres:/var/lib/postgresql/data
networks:
- outline-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U outline"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: outline-redis
restart: unless-stopped
command: redis-server --save 60 1 --loglevel warning
volumes:
- ./data/redis:/data
networks:
- outline-net
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
mailhog:
image: mailhog/mailhog
container_name: outline-mailhog
restart: unless-stopped
ports:
- "8025:8025"
networks:
- outline-net
networks:
outline-net:
driver: bridge
EOF
docker compose config --quiet && echo " docker-compose.yml 作成完了"
# ================================================================
# STEP 6: バックアップスクリプト作成
# ================================================================
echo "------------------------------------------------"
echo " STEP 6: バックアップスクリプト作成"
echo "------------------------------------------------"
cat > "$INSTALL_DIR/outline-backup.sh" <<'BACKUP_SCRIPT'
#!/bin/bash
# Outline バックアップスクリプト
set -e
INSTALL_DIR="/opt/docker/outline"
BACKUP_DIR="$INSTALL_DIR/backups"
DATE=$(date '+%Y%m%d_%H%M%S')
BACKUP_NAME="outline_backup_${DATE}"
KEEP_DAYS=7
echo "================================================"
echo " Outline バックアップ開始: $DATE"
echo "================================================"
mkdir -p "$BACKUP_DIR/$BACKUP_NAME"
# PostgreSQL ダンプ
echo "▼ データベースをバックアップしています..."
docker exec outline-postgres pg_dump -U outline outline \
> "$BACKUP_DIR/$BACKUP_NAME/database.sql"
echo " データベースダンプ完了"
# ストレージバックアップ
echo "▼ ストレージをバックアップしています..."
tar -czf "$BACKUP_DIR/$BACKUP_NAME/storage.tar.gz" \
-C "$INSTALL_DIR/data" storage
echo " ストレージバックアップ完了"
# .env バックアップ
cp "$INSTALL_DIR/.env" "$BACKUP_DIR/$BACKUP_NAME/.env"
echo " .env バックアップ完了"
# tar でまとめて圧縮
tar -czf "$BACKUP_DIR/${BACKUP_NAME}.tar.gz" \
-C "$BACKUP_DIR" "$BACKUP_NAME"
rm -rf "$BACKUP_DIR/$BACKUP_NAME"
# 古いバックアップを削除
find "$BACKUP_DIR" -name "outline_backup_*.tar.gz" \
-mtime +${KEEP_DAYS} -delete
BACKUP_SIZE=$(du -sh "$BACKUP_DIR/${BACKUP_NAME}.tar.gz" | cut -f1)
echo ""
echo "================================================"
echo " バックアップ完了!"
echo " ファイル: $BACKUP_DIR/${BACKUP_NAME}.tar.gz"
echo " サイズ : $BACKUP_SIZE"
echo " 保持数 : $(ls $BACKUP_DIR/outline_backup_*.tar.gz 2>/dev/null | wc -l) 件"
echo "================================================"
BACKUP_SCRIPT
chmod +x "$INSTALL_DIR/outline-backup.sh"
echo " outline-backup.sh 作成完了"
# ================================================================
# STEP 7: リストアスクリプト作成
# ================================================================
echo "------------------------------------------------"
echo " STEP 7: リストアスクリプト作成"
echo "------------------------------------------------"
cat > "$INSTALL_DIR/outline-restore.sh" <<'RESTORE_SCRIPT'
#!/bin/bash
# Outline リストアスクリプト
# 使い方:
# sudo bash outline-restore.sh # 最新バックアップを自動選択
# sudo bash outline-restore.sh /path/to/backup.tar.gz # ファイルを直接指定
set -e
INSTALL_DIR="/opt/docker/outline"
BACKUP_DIR="$INSTALL_DIR/backups"
# バックアップファイルの決定
if [ -n "$1" ]; then
BACKUP_FILE="$1"
else
BACKUP_FILE=$(ls -t "$BACKUP_DIR"/outline_backup_*.tar.gz 2>/dev/null | head -1)
if [ -z "$BACKUP_FILE" ]; then
echo "エラー: バックアップファイルが見つかりません: $BACKUP_DIR"
exit 1
fi
BACKUP_SIZE=$(du -sh "$BACKUP_FILE" | cut -f1)
BACKUP_DATE=$(stat -c '%y' "$BACKUP_FILE" | cut -d'.' -f1)
echo "================================================"
echo " 最新バックアップを検出しました"
echo ""
echo " ファイル: $(basename $BACKUP_FILE)"
echo " サイズ : $BACKUP_SIZE"
echo " 作成日時: $BACKUP_DATE"
echo "================================================"
echo ""
fi
if [ ! -f "$BACKUP_FILE" ]; then
echo "エラー: ファイルが見つかりません: $BACKUP_FILE"
exit 1
fi
echo "================================================"
echo " Outline リストア開始"
echo " 対象: $BACKUP_FILE"
echo "================================================"
echo ""
read -rp "本当にリストアしますか?現在のデータは上書きされます。(yes/no): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
echo "キャンセルしました。"
exit 0
fi
# 展開
WORK_DIR=$(mktemp -d)
echo "▼ バックアップを展開しています..."
tar -xzf "$BACKUP_FILE" -C "$WORK_DIR"
BACKUP_NAME=$(ls "$WORK_DIR")
BACKUP_PATH="$WORK_DIR/$BACKUP_NAME"
echo " 展開完了"
# outline コンテナを停止
echo "▼ outline コンテナを停止しています..."
docker compose -f "$INSTALL_DIR/docker-compose.yml" stop outline
echo " 停止しました"
# データベースリストア(-d postgres で currently open database エラーを回避)
echo "▼ データベースをリストアしています..."
docker exec -i outline-postgres psql -U outline -d postgres \
-c "DROP DATABASE IF EXISTS outline;"
docker exec -i outline-postgres psql -U outline -d postgres \
-c "CREATE DATABASE outline;"
docker exec -i outline-postgres psql -U outline -d outline \
< "$BACKUP_PATH/database.sql"
echo " データベースリストア完了"
# ストレージリストア
echo "▼ ストレージをリストアしています..."
rm -rf "$INSTALL_DIR/data/storage"
tar -xzf "$BACKUP_PATH/storage.tar.gz" -C "$INSTALL_DIR/data"
# UID 1001 所有に設定(sudo が必要)
sudo chown -R 1001:1001 "$INSTALL_DIR/data/storage"
echo " ストレージリストア完了"
# クリーンアップ
rm -rf "$WORK_DIR"
# outline コンテナを再起動
echo "▼ outline コンテナを再起動しています..."
docker compose -f "$INSTALL_DIR/docker-compose.yml" start outline
sleep 5
docker compose -f "$INSTALL_DIR/docker-compose.yml" ps
echo ""
echo "================================================"
echo " リストア完了!"
echo "================================================"
RESTORE_SCRIPT
chmod +x "$INSTALL_DIR/outline-restore.sh"
echo " outline-restore.sh 作成完了"
# ================================================================
# STEP 8: cron 設定
# ================================================================
echo "------------------------------------------------"
echo " STEP 8: cron 設定"
echo "------------------------------------------------"
# cron 未インストールなら自動インストール
if ! command -v crontab &>/dev/null; then
echo "▼ cron をインストールしています..."
apt-get install -y cron &>/dev/null
systemctl enable cron
systemctl start cron
echo " cron インストール完了"
fi
# 一般ユーザーのcrontabに登録(sudo -u で実行ユーザーとして登録)
CRON_JOB="0 2 * * * bash $INSTALL_DIR/outline-backup.sh >> $BACKUP_DIR/backup.log 2>&1"
( sudo -u "$REAL_USER" crontab -l 2>/dev/null | grep -v "outline-backup.sh" ; echo "$CRON_JOB" ) \
| sudo -u "$REAL_USER" crontab -
echo " cron 登録完了(毎日 2:00 AM / ユーザー: $REAL_USER)"
# ================================================================
# STEP 9: Outline 起動
# ================================================================
echo "------------------------------------------------"
echo " STEP 9: Outline 起動"
echo "------------------------------------------------"
docker compose up -d
echo ""
echo "▼ 起動を待機しています(15秒)..."
sleep 15
docker compose ps
# ================================================================
# 完了
# ================================================================
echo ""
echo "================================================"
echo " セットアップ完了!"
echo ""
echo " Outline : ${BASE_URL}"
echo " Mailhog : http://${HOST}:8025"
echo ""
echo " ログイン手順:"
echo " 1. ${BASE_URL} を開く"
echo " 2. メールアドレスを入力して送信"
echo " 3. http://${HOST}:8025 でメールを確認"
echo " 4. メール内の「Sign In」をクリック"
echo ""
echo " バックアップ:"
echo " 手動実行 : bash $INSTALL_DIR/outline-backup.sh"
echo " 自動実行 : 毎日 2:00 AM(ログ: $BACKUP_DIR/backup.log)"
echo " リストア : sudo bash $INSTALL_DIR/outline-restore.sh"
echo " cron確認 : crontab -l"
echo ""
echo " ログ確認 : docker compose -f $INSTALL_DIR/docker-compose.yml logs -f outline"
echo " 停止 : docker compose -f $INSTALL_DIR/docker-compose.yml down"
echo "================================================"
Outline http://ホスト名:3000
Mailhog http://ホスト名:8025
Immich
# ディレクトリ作成
sudo mkdir -p /opt/docker/immich
sudo chown -R $USER:docker /opt/docker/immich
sudo chmod -R 775 /opt/docker/immich
cd /opt/docker/immich
# 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}:/data
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- '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
# .env 作成
cat > .env << 'EOF'
UPLOAD_LOCATION=./library
DB_DATA_LOCATION=./postgres
IMMICH_VERSION=release
DB_PASSWORD=postgres
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
EOF
# コンテナ起動
docker compose up -d
echo "完了!http://localhost:2283 にアクセスしてセットアップしてください"
Immich http://ホスト名:2283
Nextcloud & Onlyoffice
#!/bin/bash
set -e
# === ホスト名を入力 ===
read -p "ホスト名またはIPアドレスを入力してください: " HOST_NAME
# === JWT シークレット(変更推奨) ===
JWT_SECRET="my_stable_secret_2026"
# === 共有ネットワーク作成(既存なら再利用) ===
docker network inspect onlyoffice_net >/dev/null 2>&1 \
|| docker network create onlyoffice_net
echo "[1/2] ネットワーク onlyoffice_net を確認しました"
# ============================================================
# Nextcloud
# ============================================================
sudo mkdir -p /opt/docker/nextcloud/{data,db}
sudo chown -R $USER:$USER /opt/docker/nextcloud
cd /opt/docker/nextcloud
# container_name を固定 → OnlyOffice から "nextcloud-app" で名前解決できる
cat > docker-compose.yaml << EOF
services:
db:
image: mariadb:10.6
container_name: nextcloud-db
restart: always
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
volumes:
- ./db:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_PASSWORD: nextcloudpass
MYSQL_DATABASE: nextcloud
MYSQL_USER: nextcloud
networks:
- default
app:
image: nextcloud:latest
container_name: nextcloud-app
restart: always
ports:
- "8080:80"
depends_on:
- db
volumes:
- ./data:/var/www/html
environment:
MYSQL_PASSWORD: nextcloudpass
MYSQL_DATABASE: nextcloud
MYSQL_USER: nextcloud
MYSQL_HOST: nextcloud-db
NEXTCLOUD_TRUSTED_DOMAINS: "${HOST_NAME}:8080 nextcloud-app localhost"
OVERWRITEHOST: "${HOST_NAME}:8080"
OVERWRITEPROTOCOL: http
networks:
- default
- onlyoffice_net
networks:
default:
onlyoffice_net:
external: true
EOF
docker compose up -d
echo "[1/2] Nextcloud 起動完了"
# ============================================================
# OnlyOffice
# ============================================================
sudo mkdir -p /opt/docker/onlyoffice/{logs,data,lib,db}
sudo chown -R $USER:$USER /opt/docker/onlyoffice
cd /opt/docker/onlyoffice
# .env に JWT_SECRET を書き出す
# → ヒアドキュメントを 'EOF' にしても変数が届かない問題を回避
cat > .env << EOF
JWT_SECRET=${JWT_SECRET}
EOF
# compose 本体はシングルクォートヒアドキュメント(変数展開なし)
cat > docker-compose.yaml << 'EOF'
services:
onlyoffice-docs:
image: onlyoffice/documentserver:latest
container_name: onlyoffice-docs
restart: always
stdin_open: true
tty: true
ports:
- "9000:80"
environment:
JWT_ENABLED: "true"
JWT_SECRET: "${JWT_SECRET}" # .env から自動読み込み
volumes:
- /opt/docker/onlyoffice/logs:/var/log/onlyoffice
- /opt/docker/onlyoffice/data:/var/www/onlyoffice/Data
- /opt/docker/onlyoffice/lib:/var/lib/onlyoffice
- /opt/docker/onlyoffice/db:/var/lib/postgresql
networks:
- onlyoffice_net
networks:
onlyoffice_net:
external: true
EOF
docker compose up -d
echo "[2/2] OnlyOffice 起動完了"
# ============================================================
# 完了メッセージ
# ============================================================
echo ""
echo "======================================"
echo " 起動完了"
echo "======================================"
echo ""
echo " Nextcloud : http://${HOST_NAME}:8080"
echo " OnlyOffice : http://${HOST_NAME}:9000"
echo ""
echo "======================================"
echo " Nextcloud 管理画面 → ONLYOFFICE 設定"
echo "======================================"
echo ""
echo " ドキュメントサービスのアドレス(ブラウザ用):"
echo " http://${HOST_NAME}:9000"
echo ""
echo " JWT シークレット: ${JWT_SECRET}"
echo ""
echo " 認証ヘッダー:(空白のまま)"
echo ""
echo " ドキュメントサービスの内部アドレス(サーバー間):"
echo " http://onlyoffice-docs"
echo ""
echo " Nextcloud サーバーの内部アドレス(コールバック用):"
echo " http://nextcloud-app"
echo ""
echo "======================================"
echo " 個別アップデート手順"
echo "======================================"
echo ""
echo " Nextcloud のみ更新:"
echo " cd /opt/docker/nextcloud && docker compose pull && docker compose up -d"
echo ""
echo " OnlyOffice のみ更新:"
echo " cd /opt/docker/onlyoffice && docker compose pull && docker compose up -d"
echo ""
Nextcloud http://ホスト名:8080
Onlyoffice http://ホスト名:9000
FleshRSS
mkdir -p /opt/docker/freshrss && cat > /opt/docker/freshrss/docker-compose.yml << 'EOF'
version: '3'
services:
freshrss:
image: freshrss/freshrss:latest
container_name: freshrss
restart: unless-stopped
ports:
- "6060:80"
volumes:
- freshrss_data:/var/www/FreshRSS/data
- freshrss_extensions:/var/www/FreshRSS/extensions
environment:
TZ: Asia/Tokyo
CRON_MIN: '*/15'
volumes:
freshrss_data:
freshrss_extensions:
EOF
cd /opt/docker/freshrss && docker compose up -d && sleep 10 && \
sudo apt install -y unzip && \
curl -L "https://framagit.org/nicofrand/xextension-threepanesview/-/archive/master/xextension-threepanesview-master.zip" -o tpv.zip && \
unzip tpv.zip && \
docker cp xextension-threepanesview-master freshrss:/var/www/FreshRSS/extensions/xExtension-ThreePanesView && \
rm -rf tpv.zip xextension-threepanesview-master && \
curl -L https://github.com/Niehztog/freshrss-af-readability/archive/refs/heads/master.zip -o af.zip && \
unzip af.zip && \
docker cp freshrss-af-readability-master freshrss:/var/www/FreshRSS/extensions/xExtension-af_readability && \
rm -rf af.zip freshrss-af-readability-master && \
echo "完了!http://localhost:6060 にアクセスしてセットアップしてください"
FreshRSS http://ホスト名:6060
CSS
#stream {
width: 280px !important;
min-width: 280px !important;
}
Dockhand
3333ポートに変更しています。
# ディレクトリ作成&yml編集
mkdir -p /opt/docker/dockhand && cat > /opt/docker/dockhand/docker-compose.yml << 'EOF'
services:
dockhand:
image: fnsys/dockhand:latest
container_name: dockhand
restart: unless-stopped
ports:
- 3333:3000
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- dockhand_data:/app/data
volumes:
dockhand_data:
EOF
# 起動
cd /opt/docker/dockhand && docker compose up -d
echo "完了!http://localhost:3333 にアクセスしてセットアップしてください"
Dockhand http://ホスト名:3333
Syncthing
Nextcloudの権限関連の対策も組み込んでいます。
#!/bin/bash
# =============================================================================
# Syncthing ホスト直インストールスクリプト
# 使い方: bash install-syncthing.sh (sudo不要)
# =============================================================================
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
# ── 実行ユーザー確認 ───────────────────────────────────────
[[ $EUID -eq 0 ]] && error "このスクリプトは一般ユーザーで実行してください(sudo不要)"
CURRENT_USER=$(whoami)
# ── config.xml パスを解決する関数 ─────────────────────────
find_config() {
local candidates=(
"$HOME/.local/state/syncthing/config.xml"
"$HOME/.local/share/syncthing/config.xml"
"$HOME/.config/syncthing/config.xml"
)
for p in "${candidates[@]}"; do
[[ -f "$p" ]] && echo "$p" && return
done
# 見つからなければ find で探す
find "$HOME" -name "config.xml" -path "*/syncthing/*" 2>/dev/null | head -1
}
# ── apt リポジトリ追加 & インストール ─────────────────────
info "Syncthing 公式リポジトリを追加します..."
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://syncthing.net/release-key.gpg \
| sudo tee /etc/apt/keyrings/syncthing-archive-keyring.gpg > /dev/null
echo "deb [signed-by=/etc/apt/keyrings/syncthing-archive-keyring.gpg] https://apt.syncthing.net/ syncthing stable" \
| sudo tee /etc/apt/sources.list.d/syncthing.list > /dev/null
sudo apt-get update -q || true # 無関係なリポジトリのエラーは無視
sudo apt-get install -y syncthing || error "Syncthing のインストールに失敗しました。"
success "Syncthing をインストールしました。"
# ── systemd サービス登録 ───────────────────────────────────
info "systemd サービスを登録します(ユーザー: $CURRENT_USER)..."
sudo systemctl enable syncthing@$CURRENT_USER
sudo systemctl start syncthing@$CURRENT_USER
success "Syncthing サービスを起動しました。"
# ── config.xml 生成待機(最大60秒)────────────────────────
info "config.xml の生成を待機中..."
CONFIG_FILE=""
for i in $(seq 1 30); do
CONFIG_FILE=$(find_config)
[[ -n "$CONFIG_FILE" ]] && break
sleep 2
done
[[ -z "$CONFIG_FILE" ]] && error "config.xml が見つかりませんでした。\n → find \$HOME -name config.xml -path '*/syncthing/*' で確認してください。"
success "config.xml を検出しました: $CONFIG_FILE"
# ── GUI をリモートアクセス可能に変更 ──────────────────────
info "GUI をリモートアクセス可能に設定します..."
sudo systemctl stop syncthing@$CURRENT_USER
python3 - "$CONFIG_FILE" << 'PYEOF'
import xml.etree.ElementTree as ET, sys
tree = ET.parse(sys.argv[1])
root = tree.getroot()
gui = root.find('gui')
def set_or_create(parent, tag, text):
el = parent.find(tag)
if el is None:
el = ET.SubElement(parent, tag)
el.text = text
set_or_create(gui, 'address', '0.0.0.0:8384')
tree.write(sys.argv[1], encoding='unicode', xml_declaration=True)
print("GUIアドレスを 0.0.0.0:8384 に変更しました。")
PYEOF
# ── Nextcloud ディレクトリのパーミッション設定 ────────────
info "Nextcloud ディレクトリのパーミッションを設定します..."
# Syncthing が Nextcloud のファイルを読めるよう other に r+x を付与
sudo chmod o+rx /opt/docker/nextcloud
sudo chmod o+rx /opt/docker/nextcloud/data
sudo chmod o+rx /opt/docker/nextcloud/data/data
sudo chmod o+rx /opt/docker/nextcloud/data/data/user
sudo chmod -R o+rX /opt/docker/nextcloud/data/data/user/files
# files ディレクトリ自体への書き込み権限(Send Only 運用では不要な場合も)
sudo chmod o+w /opt/docker/nextcloud/data/data/user/files
# www-data グループに追加(グループ経由でのアクセスが必要な場合)
info "ユーザー '$CURRENT_USER' を www-data グループに追加します..."
sudo usermod -aG www-data "$CURRENT_USER"
success "パーミッション設定が完了しました。"
# ── Syncthing 再起動 ───────────────────────────────────────
info "Syncthing を起動します..."
sudo systemctl start syncthing@$CURRENT_USER
sleep 3
# ── Device ID 取得 ─────────────────────────────────────────
DEVICE_ID=$(syncthing --device-id 2>/dev/null || echo "(GUIの「情報」から確認してください)")
HOST_IP=$(hostname -I | awk '{print $1}')
# ── 完了メッセージ ─────────────────────────────────────────
echo ""
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN} Syncthing インストール完了!${NC}"
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e " Web UI : ${BLUE}http://$HOST_IP:8384${NC}"
echo -e " 設定ファイル : $CONFIG_FILE"
echo -e " 実行ユーザー : $CURRENT_USER"
echo ""
echo -e " Device ID(バックアップPCへの接続時に使用):"
echo -e " ${YELLOW}$DEVICE_ID${NC}"
echo ""
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e " 次のステップ:"
echo -e " 1. Web UI にアクセスしてパスワードを設定"
echo -e " (右上メニュー → Settings → GUI → GUI Authentication)"
echo -e " 2. フォルダを追加(ホストの絶対パスをそのまま入力)"
echo -e " 例: /opt/docker/immich/library/library/admin"
echo -e " /opt/docker/notediscovery/data"
echo -e " /opt/docker/nextcloud/data/data/user/files"
echo -e " 3. 各フォルダのタイプを ${YELLOW}Send Only${NC} に設定"
echo -e " 4. バックアップPCと Device ID を交換して接続"
echo -e " 5. バックアップPC側を ${YELLOW}Receive Only${NC} + ${YELLOW}階段状バージョニング${NC} に設定"
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
echo -e "${YELLOW} ⚠ www-data グループの反映について:${NC}"
echo -e " ユーザー '$CURRENT_USER' を www-data グループに追加しましたが、"
echo -e " グループ変更はログイン中のシェルセッションには即時反映されません。"
echo -e " Syncthing サービスは systemd 経由で再起動済みのため問題ありませんが、"
echo -e " 手動で syncthing コマンドを実行する場合は一度ログアウト&ログインしてください。"
echo ""
# ── UFW 案内 ───────────────────────────────────────────────
if command -v ufw &>/dev/null && sudo ufw status | grep -q "Status: active"; then
echo -e "${YELLOW} UFW が有効です。以下でポートを開放してください:${NC}"
echo ""
echo " sudo ufw allow 8384/tcp # Web UI"
echo " sudo ufw allow 22000/tcp # Sync (TCP)"
echo " sudo ufw allow 22000/udp # Sync (QUIC)"
echo " sudo ufw allow 21027/udp # ローカル探索"
echo ""
fi
Syncthing http://ホスト名:8384

