本文へ移動
購読
GUIDE#saas更新 2026.10.02

Nextcloudで社内ファイル共有を構築する:Docker Composeでの導入と運用

Nextcloud 34をDocker Composeで構築し、setupchecksの警告解消、共有リンクの強制設定、チームフォルダー、NAS接続、バックアップまでを実機で検証した手順です。

level
中級
time
約60分
stack
Docker Compose / Nextcloud 34.0.4(apache) / MariaDB 11.8 / Redis 8.8
author
@ryoma_t
// 前提条件
Docker と docker compose が使えるLinuxサーバー / HTTPSで公開するためのリバースプロキシとDNS
Nextcloudの営業部チームフォルダーでtest.txtを共有し、パスワードと有効期限が強制された共有リンクの設定を開いた画面
画像:nagoya.dev(実際の画面・実行結果をもとに作成)

Nextcloud(ネクストクラウド)とは、ファイルの保存・同期・共有をWebブラウザーと専用クライアントで行える、自社サーバーに構築できるオープンソースのコラボレーション基盤です。OneDriveやGoogleドライブに近い使い勝手を、データを社外に出さずに実現できます。

ここではNextcloud 34.0.4(執筆時点でDocker公式イメージの production タグが指すバージョン)をMariaDBとRedisと組み合わせてDocker Composeで起動し、管理画面の警告をひとつずつ解消し、会社向けの共有ポリシー、部署ごとのチームフォルダー、NASとの接続、バックアップまでを実機で確かめた順に示します。コマンドの出力やエラーの文言は、検証環境で実際に表示されたものです。

この記事でわかること

  • NextcloudとNAS・SaaS型ストレージの違いと、Nextcloudを選ぶ条件
  • MariaDB・Redis・cronコンテナを含むcompose.yamlと、trusted_domainsの考え方
  • occ setupchecks に出る警告と、それぞれを消すコマンド(メンテナンス時間はUTCで指定)
  • 設定プリセットと共有リンクのパスワード・有効期限の強制、チームフォルダーの作り方
  • SMBでNASをつなぐときに必要な追加パッケージ、バックアップと更新の手順

Nextcloudとは:NASやSaaS型ストレージとの違い

Nextcloudは、ドイツのNextcloud GmbHが開発するAGPLv3ライセンスのソフトウェアです。ファイル共有を中心に、カレンダー・連絡先(CalDAV/CardDAV)、チャットとビデオ会議(Talk)、ブラウザーでのOffice文書編集などをアプリとして追加できます。本体は無償で使え、同社からサポート付きのエンタープライズ契約も提供されています。

「社内でファイルを共有する」という目的には、NAS、SaaS型ストレージ、Nextcloudの3つの選択肢があります。違いは主に、社外との共有と運用の負担に出ます。

観点NAS(SMB共有)SaaS型(OneDrive・Googleドライブなど)Nextcloud(セルフホスト)
データの置き場所社内ベンダーのクラウド社内または自社管理のサーバー
社外の人との共有VPNなしでは困難共有リンクで容易共有リンクで容易(公開範囲は自社で決める)
PC・スマホとの同期ドライブの割り当て中心専用クライアントで同期専用クライアントで同期、Windowsではオンデマンド取得も可
権限管理フォルダーのACL管理センターのポリシーグループ・チームフォルダー・共有ポリシー
運用の負担ディスクとバックアップほぼベンダー側OS・DB・アプリの更新まで自社

選ぶ基準ははっきりしています。Microsoft 365やGoogle Workspaceを契約済みで、データを社外に置くことに制約がなければ、付属のストレージを使うほうが運用は楽です。取引先との受け渡しが多いのにデータを社外に出せない、既存のNASを外から安全に使わせたい、ユーザー数に比例するライセンス費用を避けたい、といった条件があるときにNextcloudが候補になります。

構成を決める:AIOかComposeか、DBとキャッシュ

Nextcloudの導入方法は大きく2つです。公式が推奨する「Nextcloud All-in-One(AIO)」は、管理用コンテナがDockerのソケットを使って本体・DB・Redis・バックアップ(BorgBackup)などを自動で組み立てる方式で、更新とバックアップを画面から操作できます。もう1つは、コミュニティが保守するDocker公式イメージ(nextcloud)を自分でcompose.yamlに組む方式です。

Dockerに詳しい担当者がいない職場ならAIOが無難です。ただし管理用コンテナにDockerソケットを渡すことは、そのコンテナにホストのroot相当の権限を与えることと同じなので、他のサービスと同居するサーバーでは避けたい構成です。既存のリバースプロキシや監視、DBの運用ルールに合わせたい場合は、ここで示すCompose方式を選びます。

DBはMariaDB 11.8を使い、トランザクション分離レベルを READ-COMMITTED にします。検証では occ setupchecks の「Database transaction isolation level: Read committed」と「Database version: 11.8.9-MariaDB」がいずれも正常と判定されました。Redisはファイルロックとセッションに使い、公式イメージは環境変数 REDIS_HOST を渡すだけで、ファイルロックにRedis、ローカルキャッシュにAPCuを設定します。利用者が1人の検証環境で、待機時のメモリ使用量は本体が約180MB、MariaDBが約80MB、Redisが約8MBでした。同時利用者やプレビュー生成が増えると本体側が増えるので、仮想マシンには4GB程度を見込んでおきます。

Docker Composeで起動する

パスワードを .env に生成し、compose.yamlを置きます。

terminal
mkdir -p ~/nextcloud && cd ~/nextcloud
printf 'MARIADB_ROOT_PASSWORD=%s\nMARIADB_PASSWORD=%s\nNEXTCLOUD_ADMIN_PASSWORD=%s\n' "$(openssl rand -hex 16)" "$(openssl rand -hex 16)" "$(openssl rand -hex 12)" > .env
chmod 600 .env
compose.yaml
services:
  db:
    image: mariadb:11.8
    restart: unless-stopped
    command: --transaction-isolation=READ-COMMITTED
    environment:
      MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
      MARIADB_DATABASE: nextcloud
      MARIADB_USER: nextcloud
      MARIADB_PASSWORD: ${MARIADB_PASSWORD}
    volumes:
      - db:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 10s
      retries: 10

  redis:
    image: redis:8.8-alpine
    restart: unless-stopped

  app:
    image: nextcloud:34.0.4-apache
    restart: unless-stopped
    ports:
      - "127.0.0.1:8081:80"
    environment:
      MYSQL_HOST: db
      MYSQL_DATABASE: nextcloud
      MYSQL_USER: nextcloud
      MYSQL_PASSWORD: ${MARIADB_PASSWORD}
      REDIS_HOST: redis
      NEXTCLOUD_ADMIN_USER: ncadmin
      NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD}
      NEXTCLOUD_TRUSTED_DOMAINS: cloud.example.co.jp localhost
      PHP_UPLOAD_LIMIT: 10G
      PHP_MEMORY_LIMIT: 1G
    volumes:
      - nextcloud:/var/www/html
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  cron:
    image: nextcloud:34.0.4-apache
    restart: unless-stopped
    entrypoint: /cron.sh
    volumes:
      - nextcloud:/var/www/html
    depends_on:
      - app

volumes:
  db:
  nextcloud:

cron サービスは本体と同じイメージを /cron.sh で起動し、中のbusyboxのcrondが5分ごとに cron.php を実行します。ファイルの整理、共有の期限切れ処理、通知などのバックグラウンド処理はここで回ります。NEXTCLOUD_TRUSTED_DOMAINS には、利用者がブラウザーで開くホスト名を空白区切りで並べます。初回起動ではインストールまで自動で進むので、終わったら occ(Nextcloudの管理コマンド)で状態を確かめます。

terminal
docker compose up -d
docker compose exec -u www-data app php occ status

installed: true、version: 34.0.4.1、maintenance: false が並べば起動完了です。occ は必ず -u www-data で実行します。rootで実行すると、設定ファイルやキャッシュをrootの所有で作ってしまい、Webからの書き込みが失敗する原因になります。

管理画面の警告を消す:occ setupchecks

インストール直後は、管理画面の「概要」にセキュリティとセットアップの警告がいくつも出ます。同じ内容は occ setupchecks で一覧できるので、上から順に片付けます。検証環境で最初に出た主な警告と、対処のコマンドは次のとおりです。

表示された警告(抜粋)意味対処
Cron last run: Last background job execution ran 56 years agoバックグラウンド処理が一度も走っていないocc background:cron(cronコンテナの起動も確認)
Maintenance window start: Server has no maintenance window start time configured重い日次処理が業務時間に走るocc config:system:set maintenance_window_start --type=integer --value=16
Mimetype migrations availableファイル種別の定義が古いocc maintenance:repair --include-expensive
Default phone region: Your installation has no default phone region set国番号なしの電話番号を検証できないocc config:system:set default_phone_region --value=JP
Second factor configuration: two-factor authentication is not enforced2要素認証が任意のままocc twofactorauth:enforce --on
HTTP headers: The Strict-Transport-Security HTTP header is not setHSTSが未設定リバースプロキシでHSTSを付与

いちばん間違えやすいのはメンテナンス時間です。maintenance_window_start はUTCの「時」で指定し、その時刻から4時間の間に重い処理を寄せる設定で、同梱の config.sample.php にもUTCと明記されています。日本時間の1時から5時にしたいなら、9時間引いた 16 が正解です。1 を入れると日本時間の10時から14時になり、業務時間のど真ん中に処理が集まります。

terminal
docker compose exec -u www-data app php occ background:cron
docker compose exec -u www-data app php occ config:system:set maintenance_window_start --type=integer --value=16
docker compose exec -u www-data app php occ config:system:set default_phone_region --value=JP
docker compose exec -u www-data app php occ maintenance:repair --include-expensive
docker compose exec -u www-data app php occ twofactorauth:enforce --on
docker compose exec -u www-data app php occ setupchecks

再実行すると、上の5項目は消えました。残るのはHTTPSとHSTS、メール送信の設定など、リバースプロキシとSMTPを用意してから直す項目です。リバースプロキシの後ろに置く場合は、overwriteprotocol を https に、overwrite.cli.url を外向きのURLに、trusted_proxies にプロキシのアドレスを設定しないと、「HTTPS access and URLs: Your instance is generating insecure URLs」が消えません。公式イメージでは、それぞれ環境変数 OVERWRITEPROTOCOL、OVERWRITECLIURL、TRUSTED_PROXIES でも渡せます。

2要素認証を強制すると、WebDAVでのパスワード認証が拒否されます。検証では、強制後にログインパスワードで curl -T によるアップロードを試すと「401」「PasswordLoginForbidden」「password login forbidden」が返り、occ user:auth-tokens:add で発行したアプリパスワードでは201で成功しました。デスクトップ同期クライアントはブラウザーでのログイン経由でアプリパスワードを自動取得するため影響を受けませんが、エクスプローラーのネットワークドライブやスクリプトからのWebDAV接続は、利用者にアプリパスワードを発行してもらう運用が必要です。

会社向けの共有ポリシー:プリセットとリンクの制限

Nextcloud 34には、用途に合わせて既定値をまとめて切り替える「設定プリセット」があります。Docker公式イメージで作った直後は NONE で、共有リンクにパスワードも有効期限も要求されません。occ config:preset --list で選べるのは LARGE、MEDIUM、SMALL、SHARED、UNIVERSITY、SCHOOL、CLUB、FAMILY、PRIVATE、NONE の10種類で、--compare を付けると各プリセットで変わる設定値を表で比べられます。

terminal
docker compose exec -u www-data app php occ config:preset --compare
docker compose exec -u www-data app php occ config:preset SMALL

比較表では、SMALL・MEDIUM・LARGEの業務向けプリセットは、共有リンクのパスワード必須(shareapi_enforce_links_password)、既定の有効期限(shareapi_default_expire_date)、有効期限の強制(shareapi_enforce_expire_date)がいずれも1(有効)になっていました。実際にSMALLを適用すると、ゲストアカウント(guests)とWebAuthnの2要素認証アプリも有効化されました。プリセットは「まだ明示的に設定していない値」にだけ効くので、個別に調整したい値は後から上書きします。

terminal
docker compose exec -u www-data app php occ config:app:set core shareapi_expire_after_n_days --value=14
docker compose exec -u www-data app php occ config:list core | grep -E 'shareapi_(enforce|default|expire)'

有効期限を14日にした状態で、パスワードなしの共有リンクをAPIで作ろうとすると「Passwords are enforced for link and mail shares」で拒否され、パスワード付きで作ると有効期限が自動的に14日後の日付で設定されました。

検証では、occ で共有ポリシーを変えた直後の1回目は設定が反映されず、パスワードなしのリンクが作れてしまいました。少し時間をおいた2回目からは拒否されています。ポリシーを変えたら、しばらく待ってから実際に共有リンクを作って確かめてください。また、変更前に作られたリンクにはさかのぼって適用されない前提で、運用開始前に作ったテスト用の共有は削除しておきます。

部署ごとのフォルダー:ユーザーとチームフォルダー

ユーザーとグループは occ でも作れます。パスワードをコマンド引数に書かず、環境変数で渡すのが安全です。下の例の taro と「営業部」は、実際のユーザー名と部署名に置き換えます。

terminal
docker compose exec -u www-data -e OC_PASS='初回だけ使うパスワード' app php occ user:add --password-from-env --display-name="山田 太郎" --group="営業部" taro

グループが存在しなければ同時に作られ、「Created group "営業部"」「Account "taro" added to group "営業部"」と表示されます。個人のフォルダーを部署で共有し合う運用は、担当者が退職したときに共有が消える原因になるため、部署のファイルは「チームフォルダー」(アプリ名は groupfolders)に置きます。フォルダーはユーザーではなくNextcloud自体が所有し、グループ単位で権限と容量を決められます。

terminal
docker compose exec -u www-data app php occ app:install groupfolders
docker compose exec -u www-data app php occ groupfolders:create "営業部"
docker compose exec -u www-data app php occ groupfolders:group 1 営業部 write delete share
docker compose exec -u www-data app php occ groupfolders:quota 1 50G
docker compose exec -u www-data app php occ groupfolders:list

groupfolders:create が表示する数字がフォルダーIDで、以降のコマンドはこのIDを指定します。権限は read、write(作成と更新)、share、delete の4種類で、何も書かなければ読み取り専用です。検証では最初に create という語を並べてしまい、「Unable to parse permissions input」で失敗しました。作成権限は write に含まれています。サブフォルダー単位で権限を変えたい場合は、groupfolders:permissions で詳細権限(ACL)を有効にします。

NASとつなぐ:外部ストレージ(SMB)

既存のWindowsファイルサーバーやNASの共有フォルダーは、外部ストレージアプリ(files_external)でNextcloudの中に見せられます。いきなり全データを移さず、まずNASをつないで社外共有の窓口だけNextcloudにする、という段階的な移行に使えます。

ただし公式イメージには、SMB接続に必要な部品が入っていません。occ files_external:dependencies を実行すると「Either the php-smbclient library (preferred) or the smbclient binary is required for SMB storages.」と表示され、SMBの外部ストレージは作成できない状態でした。イメージを少し拡張して smbclient を入れます。

Dockerfile
FROM nextcloud:34.0.4-apache
RUN apt-get update \
 && apt-get install -y --no-install-recommends smbclient \
 && rm -rf /var/lib/apt/lists/*

docker build -t nextcloud-smb:34.0.4 . でイメージを作り、compose.yamlの app と cron の image: をこのイメージに置き換えて作り直すと、依存関係の表示は「The php-smbclient library provides improved compatibility and performance for SMB storages.」という推奨だけに変わり、SMBの外部ストレージを追加できるようになります。PHP拡張の smbclient まで入れると性能と互換性が上がりますが、ビルドにコンパイラーが必要になるため、まずはバイナリで始めて、大量のファイルを扱う場合に検討します。追加したイメージのサイズ増加は、検証では約40MBでした。

運用:バックアップと更新

バックアップは、メンテナンスモードで書き込みを止めてから、DBとボリュームを取得します。DBのダンプはコンテナ内の環境変数を使えば、パスワードをコマンドに書かずに済みます。

terminal
docker compose exec -u www-data app php occ maintenance:mode --on
docker compose exec db sh -c 'exec mariadb-dump --single-transaction -u"$MARIADB_USER" -p"$MARIADB_PASSWORD" "$MARIADB_DATABASE"' > nextcloud-db-$(date +%F).sql
docker run --rm -v nextcloud_nextcloud:/src:ro -v "$PWD":/backup alpine tar czf /backup/nextcloud-files-$(date +%F).tgz -C /src .
docker compose exec -u www-data app php occ maintenance:mode --off

ボリューム名は「プロジェクト名(ディレクトリ名)_ボリューム名」になるので、docker volume ls で確認してから指定します。公式イメージは /var/www/html にプログラム本体・設定・アプリ・データをまとめて置くため、ファイルが1つもない直後の状態でもアーカイブは約355MBになりました。データが増えてきたら、/var/www/html/data を別のボリュームやNFSでマウントしたストレージに分け、データ部分はストレージ側のスナップショットで守る構成にすると、バックアップの時間と容量を抑えられます。

更新は、メジャーバージョンを1つずつ上げるのが絶対の条件です。イメージの起動スクリプトは、2つ以上飛ばそうとすると「It is only possible to upgrade one major version at a time.」と表示して起動を止め、逆に古いイメージに戻そうとすると「downgrading is not supported」で止まります。執筆時点ではバージョン35.0.0が公開されたばかりで、production タグはまだ34を指していました。業務用途では production の動きに合わせ、メジャー更新の前にバックアップを取り、使っているアプリ(チームフォルダーなど)が新バージョンに対応していることを管理画面で確かめてからタグを上げます。

動作確認

最後に、利用者の目線で一連の操作が通るかを確かめます。WebDAVのアップロードには、2要素認証を強制している場合はアプリパスワードを使います。

terminal
docker compose exec -u www-data app php occ status
docker compose exec -u www-data -e NC_PASS='taroのパスワード' app php occ user:auth-tokens:add --password-from-env taro
curl -s -o /dev/null -w '%{http_code}\n' -u 'taro:<発行されたアプリパスワード>' -T test.txt 'http://127.0.0.1:8081/remote.php/dav/files/taro/%E5%96%B6%E6%A5%AD%E9%83%A8/test.txt'

最後のコマンドで 201 が返れば、そのユーザー(例ではtaro)が営業部のチームフォルダーに書き込めています(URL中の %E5%96%B6%E6%A5%AD%E9%83%A8 は「営業部」をURLエンコードしたものです)。ブラウザーで同じユーザーとしてログインし、ファイル一覧に「営業部」フォルダーが見えること、そのファイルの共有リンクを作るとパスワードの入力を求められ、有効期限が自動で入ることまで確認できれば、ここまでの設定はすべて効いています。

つまずきポイント

ブラウザーに「Access through untrusted domain」と表示される

trusted_domains に登録されていないホスト名でアクセスすると出ます。画面には「Please contact your administrator. If you are an administrator, edit the "trusted_domains" setting in config/config.php」と続きます。IPアドレスでアクセスした場合や、リバースプロキシが別名で転送している場合が典型です。occ config:system:set trusted_domains 2 --value=cloud.example.co.jp のように番号を指定して追加します。環境変数 NEXTCLOUD_TRUSTED_DOMAINS はインストール時の初期値として書き込まれるものなので、稼働後にホスト名を増やすときは occ で直接追加するのが確実です。

WebDAVやスクリプトからの接続が401「PasswordLoginForbidden」になる

2要素認証を強制した後に、ログインパスワードでWebDAVに接続すると出ます。ブラウザーの「個人設定 → セキュリティ」でアプリパスワードを作るか、管理者が occ user:auth-tokens:add で発行し、それを接続先のパスワード欄に入れます。アプリパスワードは端末ごとに分けて発行すると、紛失した端末の分だけ失効させられます。

更新後にコンテナが「It is only possible to upgrade one major version at a time.」で起動しない

イメージのタグを latest にしていて、しばらく放置した後に2世代以上まとめて上がったときに起きます。元のメジャーバージョンのタグに戻して起動し直し、1つ上のメジャー版、その次、と順に上げます。再発を防ぐため、タグは 34-apache のようにメジャー番号で固定しておきます。

SMBの外部ストレージが追加できない

公式イメージに smbclient がないことが原因で、occ files_external:dependencies に「Either the php-smbclient library (preferred) or the smbclient binary is required for SMB storages.」と出ます。前述のDockerfileで拡張したイメージに切り替えてください。Windows共有側では、Nextcloudからの接続に使う専用のサービスアカウントを作り、必要な共有だけに権限を与えます。

よくある質問

Nextcloudは無料で使えるの?

本体はAGPLv3のオープンソースで、ユーザー数の上限なく無料で使えます。費用がかかるのはサーバー、ストレージ、バックアップ先、そして運用の工数です。障害時の問い合わせ先やセキュリティ更新の長期提供が必要な場合は、Nextcloud GmbHの有償エンタープライズ契約を検討します(価格は公式サイトで確認してください)。

NASとNextcloudの違いは何ですか?

NASは社内ネットワークにファイルを置く「記憶装置」で、主にSMBでドライブとして使います。一方のNextcloudは、その上に載せる「共有と同期のアプリ」で、社外への共有リンク、スマホやPCとの同期、版管理、2要素認証を備えます。NASを保存先にしてNextcloudを窓口にする組み合わせもよく使われます。

Nextcloudにはどんな脆弱性がありますか?

見つかった脆弱性はNextcloudのセキュリティアドバイザリーで公開され、メンテナンスリリースで修正されます。バグ報奨金制度もあり、報告は継続的に出ているため、マイナー更新を毎月当てる運用が前提です。設定面では、公式のセキュリティスキャンでヘッダーやバージョンの問題を外部から確認できます。

Nextcloudの代わりになる製品は?

セルフホストならownCloud(Go言語で書き直したInfinite Scale)やSeafile、クラウドで良ければMicrosoft 365のOneDrive/SharePoint、Googleドライブ、Boxが候補です。Office文書の共同編集と権限管理を重視するなら既存のSaaS、データを社内に置くことが条件ならNextcloudかSeafile、というように要件の優先順位で選びます。

まとめ

Nextcloudは、Compose構成ならMariaDB(READ-COMMITTED)、Redis、cronコンテナまで含めて起動し、occ setupchecks の警告を順に消すところまでが最初の作業です。メンテナンス時間はUTCで16を指定し、共有ポリシーは設定プリセットのSMALL以上を起点にします。部署のデータはチームフォルダーに置き、NAS接続にはsmbclientを追加したイメージを使います。更新はメジャー版を1つずつ、バックアップを取ってから進めてください。

次に読む

$ subscribe --weekly
毎週月曜、企業ITとホームラボの要点を5分で。