<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Nextcloud on Off-Grid</title><link>https://offgrid.taikiito.com/tags/nextcloud/</link><description>Recent content in Nextcloud on Off-Grid</description><generator>Hugo</generator><language>ja-JP</language><lastBuildDate>Mon, 05 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://offgrid.taikiito.com/tags/nextcloud/index.xml" rel="self" type="application/rss+xml"/><item><title>ファイルをGoogle DriveからNextcloudに移す</title><link>https://offgrid.taikiito.com/guides/google-drive-to-nextcloud/</link><pubDate>Sat, 12 Sep 2026 00:00:00 +0000</pubDate><guid>https://offgrid.taikiito.com/guides/google-drive-to-nextcloud/</guid><description>Google Driveのファイルを自前サーバー(Nextcloud)に移行した時の記録。docker-composeでのNextcloud構築、Cloudflare Tunnelでの公開、rcloneでのバックアップ設定まで、同じ環境を再現できる粒度で書いた。</description><content:encoded><![CDATA[<p>Google Driveのファイルを自分のサーバー上のNextcloudというソフトに移した記録。前回の版は経緯中心だったので、今回は実際に使ったdocker-composeの設定内容やコマンドまで書き足した。<strong>この記事だけで、Nextcloudを自分のサーバーに構築するところまで再現できることを目指している。</strong></p>
<h2 id="前提">前提</h2>
<p>以下がすでに用意できていることを前提にする。まだの場合は先に<a href="/guides/self-hosting-basics/">自分の「サーバー」を持つとはどういうことか</a>を読んでほしい。</p>
<ul>
<li>常時起動しているLinux環境(Docker Engineが使える状態)</li>
<li>自分名義のドメイン、およびCloudflare(無料プランで可)でそのドメインを管理していること</li>
<li>Cloudflare Tunnel(<code>cloudflared</code>というソフトを使い、自宅のルーターの設定を一切いじらずに自宅サーバーを安全にインターネット公開する仕組み)がすでに動いている状態。導入手順は<a href="/guides/self-hosting-basics/">自分の「サーバー」を持つとはどういうことか</a>を参照</li>
</ul>
<h2 id="何をしたかったか">何をしたかったか</h2>
<p>写真をImmichに移した(<a href="/guides/google-photos-to-immich/">前の記事</a>)のと同じ理由で、Google Driveに置きっぱなしにしていたファイルも自分の手元に戻したいと思った。写真ほど量は多くない(実質100〜150MB程度)が、契約書や身分証のコピーなど「人に預けたくないもの」が混ざっていたのが動機だった。</p>
<h2 id="step-1-nextcloudを選び全部は移さないと決めた">Step 1: Nextcloudを選び、全部は移さないと決めた</h2>
<p>Google Driveの代わりになる自前ホスト型のソフトはいくつかある。今回は次の4つを比べた。</p>
<ul>
<li><strong>Nextcloud</strong> — ファイル同期に加えて、連絡先・カレンダー・メモアプリなど周辺機能が豊富。後から機能を足しやすい。</li>
<li><strong>Seafile</strong> — ファイル同期に特化していて速いという評判だが、周辺機能はNextcloudほど無い。</li>
<li><strong>Syncthing</strong> — サーバーを介さず端末同士が直接同期する仕組み。管理はシンプルだが、Google Driveのような「どこからでもブラウザで見る」用途には向かない。</li>
<li><strong>Filestash</strong> — 既存のストレージ(自分のNASなど)にWeb UIを被せるツール。今回は土台のストレージ自体を新規に用意する必要があったので、ここでは選択肢から外れた。</li>
</ul>
<p>最終的に、**拡張性(今後、連絡先やカレンダーも同じ場所に集約したくなる見込みがあった)**を決め手にNextcloudにした。実際にこの後、<a href="/guides/icloud-google-to-nextcloud-contacts-calendar/">連絡先・カレンダーも同じNextcloudに移す</a>ことになったので、この判断は結果的に正しかった。</p>
<p>写真の時は「全部Immichに取り込む」という単純な方針にしたが、Google Driveは事情が違った。中身を確認すると、自分のファイルの他に、家族と共有しているフォルダが2つ(iPhoneの体組成計・血圧データを自動書き出しする仕組みと、ビザ手続き関連の共有フォルダ)混ざっていた。この2つは「相手(連携先アプリや家族)がGoogle前提で動いている」ため、無理にNextcloudへ移すと連携が壊れる。<strong>移行の効果(データ主権が増える度合い)と、移すコスト・壊れるリスクを天秤にかけて、この2つはGoogle側に残すと決めた。</strong> 残りの自分専用ファイル(契約書などの<code>archive</code>フォルダ)だけをNextcloudに移した。</p>
<h2 id="step-2-docker-composeでnextcloudを構築する">Step 2: docker-composeでNextcloudを構築する</h2>
<p>作業用フォルダを作り、<code>docker-compose.yml</code>を用意する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir ~/nextcloud <span class="o">&amp;&amp;</span> <span class="nb">cd</span> ~/nextcloud
</span></span></code></pre></div><p><code>docker-compose.yml</code>の中身(Nextcloud本体<code>app</code>と、データを整理して保存しておくデータベースソフト<code>db</code>〈MariaDB〉の2つのコンテナをセットで起動する構成):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">db</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">mariadb:11</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./db:/var/lib/mysql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">MYSQL_ROOT_PASSWORD=&lt;自分で決めた強力なパスワード&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">MYSQL_PASSWORD=&lt;自分で決めた強力なパスワード&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">MYSQL_DATABASE=nextcloud</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">MYSQL_USER=nextcloud</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">app</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">nextcloud:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">127.0.0.1</span><span class="p">:</span><span class="m">8081</span><span class="p">:</span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">links</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">db</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">./data:/var/www/html</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">MYSQL_PASSWORD=&lt;自分で決めた強力なパスワード&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">MYSQL_DATABASE=nextcloud</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">MYSQL_USER=nextcloud</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">MYSQL_HOST=db</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">NEXTCLOUD_ADMIN_USER=admin</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">NEXTCLOUD_ADMIN_PASSWORD=&lt;自分で決めた強力なパスワード&gt;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">NEXTCLOUD_TRUSTED_DOMAINS=drive.example.com</span><span class="w">
</span></span></span></code></pre></div><p><code>ports</code>を<code>127.0.0.1:8081:80</code>(自分のマシンの中からしかアクセスできない設定)にしているのがポイント。外部への公開はこのあとCloudflare Tunnel経由で行うので、Nextcloud自体を直接インターネットに晒す必要はない。<code>NEXTCLOUD_TRUSTED_DOMAINS</code>には、実際に使う自分のドメイン(例: <code>drive.taikiito.com</code>)を入れる。</p>
<p>起動する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker compose up -d
</span></span><span class="line"><span class="cl">docker compose ps
</span></span></code></pre></div><p><code>app</code>・<code>db</code>の2つが起動していることを確認したら、<code>http://localhost:8081</code>にアクセスして管理者アカウント(<code>NEXTCLOUD_ADMIN_USER</code>/<code>NEXTCLOUD_ADMIN_PASSWORD</code>で指定した内容)でログインできることを確認する。</p>
<p><strong>Nextcloudの管理者ユーザー名は一度決めると後から変更できない</strong>ので、<code>admin</code>のような分かりやすい名前で最初から決めておくとよい。</p>
<h2 id="step-3-cloudflare-tunnelで公開する">Step 3: Cloudflare Tunnelで公開する</h2>
<p><code>/etc/cloudflared/config.yml</code>(Cloudflare Tunnelの設定ファイル)の<code>ingress</code>(どのドメイン宛のアクセスを、サーバー内のどのポートに転送するかのルール一覧)に、Nextcloud用の行を追記する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">ingress</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">hostname</span><span class="p">:</span><span class="w"> </span><span class="l">drive.taikiito.com</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">service</span><span class="p">:</span><span class="w"> </span><span class="l">http://localhost:8081</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># ...他のサービスの行がここに続く...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">service</span><span class="p">:</span><span class="w"> </span><span class="l">http_status:404</span><span class="w">
</span></span></span></code></pre></div><p><strong>注意点</strong>: <code>service:</code>行のインデント(行頭の空白)は他の行と正確に揃えること。ここが1マスでもずれるとYAML(設定ファイルの記述形式)の解析エラーになり、Cloudflare Tunnel全体が落ちて他のサービスまで巻き込んで止まる、という事故を実際に経験している。編集後は<code>cloudflared tunnel ingress validate</code>で文法チェックしてから<code>sudo systemctl restart cloudflared</code>で反映するのが安全。</p>
<p>DNS側は、Cloudflareの管理画面(またはAPI)で<code>drive</code>(サブドメイン部分)のCNAMEレコードを<code>&lt;トンネルID&gt;.cfargotunnel.com</code>に向ける。反映後、<code>https://drive.taikiito.com</code>でNextcloudのログイン画面が表示されれば成功。</p>
<p><strong>Nextcloud特有の注意点</strong>: Nextcloud(内部でApacheというソフトを使っている)は、自分がHTTPS経由でアクセスされていることを正しく認識できないと、ログイン後に無限リダイレクトを起こすことがある。Cloudflare Tunnelは末端(サーバーとの接続)を暗号化しないHTTP接続で待ち受けるため、この設定が必要になる場合がある。症状が出た場合は、Nextcloudの<code>config/config.php</code>に以下を追記して回避する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="s1">&#39;overwriteprotocol&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;https&#39;</span><span class="p">,</span>
</span></span></code></pre></div><h2 id="step-4-macと同期する">Step 4: Macと同期する</h2>
<p>ここからは、サーバーさえ用意できていれば誰でもできる部分。Google DriveにはMacの「Finder」(ファイル一覧の画面)に統合される専用アプリがあったが、Nextcloudにも同じような公式デスクトップアプリがある。これをインストールしてログインすると、Finder上にNextcloudのフォルダが表示され、Google Driveの時とほぼ同じ感覚でファイルを開いたり保存したりできる。同期は「全部」ではなく<strong>フォルダ単位で選べる</strong>。最初はNextcloudが用意するお試し用のサンプルフォルダ(Documents、Photos、Templatesなど)が並んでいたが、これは削除してよい。本物のデータが入っているのは、移行した<code>archive</code>フォルダだけだった。</p>
<h2 id="step-5-バックアップを設定する">Step 5: バックアップを設定する</h2>
<p>「移した先が壊れたら意味がない」ので、移行作業そのものと同じくらいバックアップ体制を大事にしている。<code>rclone</code>(色々なオンラインストレージをコマンドラインから操作できるツール)を使い、Backblaze B2という安価なオンラインストレージへ、Nextcloudのデータを毎日自動でコピーする設定にした。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># データベースのダンプ</span>
</span></span><span class="line"><span class="cl">docker compose -f ~/nextcloud/docker-compose.yml <span class="nb">exec</span> -T db <span class="se">\
</span></span></span><span class="line"><span class="cl">  mysqldump -u nextcloud -p&lt;パスワード&gt; nextcloud <span class="p">|</span> gzip &gt; /tmp/nextcloud-db.sql.gz
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ファイル本体の同期</span>
</span></span><span class="line"><span class="cl">sudo rclone --config /home/ito-t/.config/rclone/rclone.conf sync <span class="se">\
</span></span></span><span class="line"><span class="cl">  ~/nextcloud/data <span class="s2">&#34;</span><span class="nv">$B2_REMOTE</span><span class="s2">/nextcloud/data&#34;</span>
</span></span></code></pre></div><p>これを日次のcron(定期実行の仕組み)に登録しておけば、毎晩自動でバックアップが取られる。</p>
<h2 id="つまづいた所積み残し">つまづいた所・積み残し</h2>
<p><strong>9.68GBの謎</strong>: 作業の途中、Googleのストレージ画面で「Driveが9.68GB使用中」と表示されているのに気づいた。しかし実際に移行した中身は100〜150MB程度しかない。90倍以上の差があり、計算が全く合わなかった。調べてみると、以前どこかのタイミングで大きなファイルを削除した際、<strong>ゴミ箱を空にしていなかった</strong>可能性が濃厚だという所まで辿り着いた。Googleドライブのゴミ箱は自動では空にならず、手動で「ゴミ箱を空にする」を押さない限り容量を圧迫し続ける。ここは正直に書いておくと、<strong>この謎は最終的に自分の手では確認しきれず、保留のまま</strong>になっている。</p>
<p><strong>家族との共有</strong>: Google Driveで便利だったことの一つに、家族とのフォルダ共有がある。Nextcloudでも同じことは可能で、家族専用アカウントを作って権限を細かく設定する方法と、アカウント不要のリンク共有で手軽に済ませる方法の2通りがある。これは記事執筆時点でまだどちらにするか決めきれていない、<strong>現在進行系の課題</strong>として正直に残しておく。</p>
<h2 id="結果">結果</h2>
<p>自分専用のファイルはNextcloud経由でMacからGoogle Driveと変わらない感覚で使えるようになった。家族共有フォルダ2つは「無理に移さない」という判断のままGoogleに残っており、これはこれで納得している状態。</p>
<p>次は、同じNextcloudに連絡先とカレンダーも集約した話。→ <a href="/guides/icloud-google-to-nextcloud-contacts-calendar/">連絡先とカレンダーをiCloud・GoogleからNextcloudに移す</a></p>
<p><strong>更新履歴</strong> — 2026-09-12: 初版 / 2026-09-13: docker-compose設定・Cloudflare Tunnel公開手順・バックアップコマンドを追記し、再現可能なレベルに書き直し</p>
]]></content:encoded></item><item><title>連絡先とカレンダーをiCloud・GoogleからNextcloudに移す</title><link>https://offgrid.taikiito.com/guides/icloud-google-to-nextcloud-contacts-calendar/</link><pubDate>Sat, 12 Sep 2026 00:00:00 +0000</pubDate><guid>https://offgrid.taikiito.com/guides/icloud-google-to-nextcloud-contacts-calendar/</guid><description>iCloud連絡先81件・Google連絡先553件をNextcloud Contactsへ、Googleカレンダーの個人予定232件をNextcloud Calendarへ移した記録。アプリパスワードの発行方法、CalDAV/CardDAVへ直接PUTする実際のコマンドまで書いた。</description><content:encoded><![CDATA[<p>連絡先とカレンダーを、Nextcloudの標準機能(Contacts・Calendar)に一本化した記録。すでにファイル(<a href="/guides/google-drive-to-nextcloud/">前の記事</a>)で使っていたNextcloudに機能を足すだけなので、新しいサーバーを立てる作業は無かった。件数が多かった分、Web UIのインポート機能が信頼できず、結局CalDAV(カレンダーの同期方法を定めた業界標準規格)・CardDAV(連絡先の同期方法を定めた業界標準規格)という仕組みへ直接データを送り込む方式に切り替えることになった。</p>
<h2 id="前提">前提</h2>
<p>すでにNextcloudが動いていることを前提にする。まだの場合は先に<a href="/guides/google-drive-to-nextcloud/">ファイルをGoogle DriveからNextcloudに移す</a>を読んでほしい。</p>
<h2 id="何をしたかったか">何をしたかったか</h2>
<p>連絡先はiCloudとGoogleの両方に分散していた。カレンダーは個人の予定をGoogleカレンダーで管理していた。どちらも「今後もGoogle/Appleに預け続けるほどの理由がない」データなので、ファイルや写真と同じ流れでNextcloudに集約することにした。同期方式は特定の会社に縛られない業界標準の規格(CardDAV・CalDAV)なので、Mac標準の「連絡先」「カレンダー」アプリやiPhoneからもそのまま繋げられる。</p>
<h2 id="step-1-nextcloudのアプリパスワードを発行する">Step 1: Nextcloudのアプリパスワードを発行する</h2>
<p>まず、CardDAV/CalDAV経由でアクセスするための専用パスワードを発行する。Nextcloud本体のログインパスワードをそのまま外部アプリに渡すのは避け、用途ごとに無効化できる「アプリパスワード」を使うのが基本。</p>
<p>Nextcloudの画面右上のアイコン → <strong>個人設定(Personal settings)</strong> → <strong>セキュリティ(Security)</strong> → 「新しいアプリパスワードを作成」から、名前(例: <code>contacts-migration</code>)を付けて発行する。表示されたパスワードは、この画面を閉じると二度と表示されないので、一時的にメモしておく。</p>
<h2 id="step-2-連絡先をエクスポートする633件">Step 2: 連絡先をエクスポートする(633件)</h2>
<p>Google連絡先553件・iCloud連絡先81件を、それぞれvCard形式(連絡先データの標準的なファイル形式、拡張子<code>.vcf</code>)で書き出す。</p>
<ul>
<li><strong>Google連絡先</strong>: <a href="https://contacts.google.com">contacts.google.com</a> → 左メニュー「エクスポート」→ vCard形式を選択</li>
<li><strong>iCloud連絡先</strong>: <a href="https://icloud.com">icloud.com</a> → 連絡先 → 全て選択 → 右下の歯車アイコン → 「vCardを書き出す」</li>
</ul>
<p>重複1件を除いた633件を、まとめた1つの<code>.vcf</code>ファイルにしておく(vCard形式は複数の連絡先を1ファイルに連結できる)。</p>
<h2 id="step-3-carddavへ直接putする">Step 3: CardDAVへ直接PUTする</h2>
<p>Nextcloud Web UI(ブラウザ上の管理画面)の「連絡先をインポート」機能も試したが、件数が多いと不安定だったため、CardDAVのエンドポイント(データの送り先URL)へ、1件ずつ<code>PUT</code>(データをサーバーへ送り込む処理)する方式に切り替えた。</p>
<p>まず、まとめた<code>.vcf</code>ファイルを連絡先1件ずつのファイルに分割する(Pythonの<code>vobject</code>ライブラリや、<code>csplit</code>のようなテキスト分割コマンドで、<code>BEGIN:VCARD</code>〜<code>END:VCARD</code>の区切りごとに切り出す)。分割できたら、1件ずつ以下のようにアップロードする。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -u <span class="s2">&#34;admin:&lt;Step1のアプリパスワード&gt;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -X PUT <span class="se">\
</span></span></span><span class="line"><span class="cl">  --data-binary @contact-001.vcf <span class="se">\
</span></span></span><span class="line"><span class="cl">  -H <span class="s2">&#34;Content-Type: text/vcard; charset=utf-8&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;https://drive.taikiito.com/remote.php/dav/addressbooks/users/admin/contacts/contact-001.vcf&#34;</span>
</span></span></code></pre></div><p>これを633件分、シェルスクリプトの<code>for</code>ループなどで繰り返す。1件ずつリソースとしてサーバーに送り込む地道なやり方だが、Web UIのインポートより確実に反映される。</p>
<h2 id="step-4-macで連絡先を同期する">Step 4: Macで連絡先を同期する</h2>
<p>Macの「連絡先」アプリ → 環境設定 → アカウント → 「アカウントを追加」→「他のアカウント」→「CardDAVアカウントを追加」。</p>
<p>ここでハマったポイントがある。macOSは通常、サーバーアドレスを短く入力するだけで<code>.well-known/carddav</code>という決まったパスへ自動でアクセスし、正しいCardDAVのURLへリダイレクトしてくれる仕組みになっている。ところが、Cloudflare経由のリダイレクト処理の影響で、この自動検出がうまく機能しなかった。</p>
<p><strong>解決策</strong>: サーバーアドレスの欄に、自動検出用の短いURL(<code>drive.taikiito.com</code>)ではなく、以下のフルパスを直接入力する。</p>
<pre tabindex="0"><code>https://drive.taikiito.com/remote.php/dav/principals/users/admin/
</code></pre><p>ユーザー名は<code>admin</code>(Nextcloudのユーザー名)、パスワードはStep 1で発行したアプリパスワードを使う。iPhoneの「連絡先」アプリでも同じ設定項目がある。</p>
<h2 id="step-5-カレンダーの移行232件">Step 5: カレンダーの移行(232件)</h2>
<p>個人用のGoogleカレンダーに入っていた予定(2019〜2029年、232件)をNextcloud Calendarの「Personal」カレンダーへ移す。</p>
<p>まず<a href="https://calendar.google.com">calendar.google.com</a>の設定画面から、対象のカレンダーを<code>.ics</code>ファイル(カレンダーの予定データをやり取りするための標準的なファイル形式)としてエクスポートする。</p>
<p>最初はNextcloud Web UIの「カレンダーをインポート」機能で<code>.ics</code>ファイルをそのまま読み込ませようとしたが、「could not be parsed」(うまく読み取れなかった、というエラーメッセージ)というエラーで通らなかった。<code>.ics</code>ファイルの行折り返し(RFC5545というカレンダーデータの技術仕様で決められたルール)を手直ししても解決しなかったため、連絡先と同じくCalDAVへ直接PUTする方式に切り替えた。</p>
<p><code>.ics</code>ファイルを1イベントずつに分割し(<code>BEGIN:VEVENT</code>〜<code>END:VEVENT</code>の区切りで切り出す)、以下のようにアップロードする。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -u <span class="s2">&#34;admin:&lt;アプリパスワード&gt;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  -X PUT <span class="se">\
</span></span></span><span class="line"><span class="cl">  --data-binary @event-001.ics <span class="se">\
</span></span></span><span class="line"><span class="cl">  -H <span class="s2">&#34;Content-Type: text/calendar; charset=utf-8&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;https://drive.taikiito.com/remote.php/dav/calendars/users/admin/personal/event-001.ics&#34;</span>
</span></span></code></pre></div><p>232件全てこの方式でアップロードしたところ、全て成功した。反映を確認したうえで、Google Calendar側の元データは削除した。ただし、妻と共有しているカレンダーはGoogleに残す判断にした(相手がGoogle前提で使っているため)。ファイル移行の時の「移す理由が移さない理由に勝つものだけ移す」という基準を、ここでも踏襲した形だ。</p>
<h2 id="つまづいた所">つまづいた所</h2>
<p><strong>1. Web UIの一括インポートは大量データに弱い</strong>: 連絡先・カレンダーともに、NextcloudのWeb UIインポート機能は件数が多いと信頼できなかった。少数なら問題ないと思うが、数百件規模ならCalDAV/CardDAVへ直接PUTする方式を最初から選んだ方が早い、というのが今回の教訓。</p>
<p><strong>2. <code>.well-known/carddav</code>のリダイレクトが失敗する</strong>: 上記Step 4の通り、自動検出をあきらめてフルパスを直接指定することで解決した。同じ症状はCalDAV側(<code>.well-known/caldav</code>)でも起こりうるので、うまくいかない場合は同じ対処(フルパス直接指定)を試すとよい。</p>
<p>積み残しもある。祖父の命日のような毎年繰り返す予定は、Google側で表示されていた2025〜2029年分のインスタンスだけを削除しており、繰り返しルールそのもの(マスター)は消していない。2030年以降にまた自動で出現する可能性があるため、気になったら改めてGoogle Calendar側で対応する必要がある。また、Gmailにはフライトやレストランの予約確認メールを自動でGoogleカレンダーに追加してくれる機能があるが、これは自前のメール+Nextcloudカレンダーの組み合わせには存在しない、Google独自の仕組みで、メール・カレンダーのどちらか片方だけ移しても再現できない。同等の仕組みを自作するなら別プロジェクトになるので、今回は見送っている。</p>
<h2 id="結果">結果</h2>
<p>連絡先633件・カレンダー232件、どちらも移行完了。MacとiPhoneから、Google/iCloudの時とほぼ変わらない感覚で使えている。大量データの移行はWeb UIのインポートを過信せず、CalDAV/CardDAVへの直接PUTを検討するとよい、というのが一番の学びだった。</p>
<p>次は、メモをJoplinに移そうとして、結局全部は移さなかった話。→ <a href="/guides/icloud-notes-to-joplin/">メモをiCloud NotesからJoplinに移す</a></p>
<p><strong>更新履歴</strong> — 2026-09-12: 初版 / 2026-09-13: アプリパスワードの発行手順・実際のcurl PUTコマンドを追記し、再現可能なレベルに書き直し</p>
]]></content:encoded></item><item><title>予定表と住所録を、全部入りのNextcloudから軽いRadicaleへ切り出す</title><link>https://offgrid.taikiito.com/guides/nextcloud-to-radicale/</link><pubDate>Mon, 05 Oct 2026 00:00:00 +0000</pubDate><guid>https://offgrid.taikiito.com/guides/nextcloud-to-radicale/</guid><description>予定236件・連絡先575件をNextcloudからRadicaleへ移した記録。データは.ics/.vcfの素のファイルになり速くなったが、Web画面と共有機能は失う。選ぶ前に知るべき制約として「Radicaleは重いREPORT問い合わせを同時に受けられない(並列5本で全滅、直列なら1本4.7秒)」という実測も書いた。</description><content:encoded><![CDATA[<p><a href="/guides/icloud-google-to-nextcloud-contacts-calendar/">前の記事</a>で、連絡先とカレンダーをiCloud・GoogleからNextcloudへ移した。それを今度はNextcloudから引き剥がして、Radicaleという「予定表と住所録しかできない」小さなサーバーへ移した記録。</p>
<p>先に正直に書くと、<strong>Nextcloudで困っていたことは何も無かった</strong>。故障の修理ではなく、作りを良くするための工事だ。だから得たものと同じ分量で、失ったものを書く。</p>
<h2 id="前提">前提</h2>
<ul>
<li>常時起動しているLinux環境とDocker(ソフトを1つずつ箱に入れて動かす仕組み)が使えること。土台は<a href="/guides/self-hosting-basics/">自分の「サーバー」を持つとはどういうことか</a></li>
<li>すでにNextcloudの連絡先・カレンダーを使っている状態(<a href="/guides/icloud-google-to-nextcloud-contacts-calendar/">前の記事</a>)</li>
<li>CalDAV(予定表をやりとりするための標準的な通信規約)・CardDAV(住所録用の同じもの)の名前だけ知っていれば十分</li>
</ul>
<h2 id="動かした理由は規模が不釣り合いの一点">動かした理由は「規模が不釣り合い」の一点</h2>
<p>移す前に、Nextcloud側の中身を<code>?export</code>で実測した。住所録<strong>575件</strong>、予定表<strong>236件</strong>、そして<strong>空の予定表が2つ</strong>(片方はiPhoneのリマインダーが勝手に作ったもの)。</p>
<p>🔴 前の記事に書いた「連絡先633件・予定232件」は誤りだった。<strong>移した件数は、移した日に書き留めただけでは当てにならない。</strong></p>
<p>実データの合計は<strong>約230KB</strong>。テキストファイル1枚に収まる量だ。これに対してNextcloudはPHPで書かれた大きな母屋にデータベースまで抱えている。やっている仕事に対して仕掛けが大きすぎる、というのが唯一の動機だった。</p>
<h2 id="radicaleは何で何ではないか">Radicaleは何で、何ではないか</h2>
<p>Radicaleは<strong>CalDAV/CardDAVだけを話す小さなサーバー</strong>。データベースを持たず、予定1件が<code>.ics</code>ファイル1枚、連絡先1件が<code>.vcf</code>ファイル1枚として、普通のフォルダにただ並んでいるだけ。</p>
<p>やっているのは「フォルダのテキストファイルを、CalDAVという会話の形に翻訳して端末に見せる」中継だけだ。Appleのカレンダーアプリには「このフォルダを見ろ」という設定が存在せず、CalDAVでしか話せないので、この翻訳役が必要になる。あとは各予定にetag(版番号)を付けて差分と衝突を管理するだけ。</p>
<p><strong>何ではないか</strong>のほうが重要だと思う。</p>
<ul>
<li><strong>使えるWeb画面が無い</strong>。ブラウザで開いても予定を見る画面は出てこない。どうしても画面が欲しければ、CalDAVを外から繋げられるメールサービス(自分はFastmail)を窓口にする手はある。</li>
<li><strong>共有リンクも共有のUIも無い</strong>。「この予定表をあの人に見せる」をボタンで作れない。</li>
<li><strong>ファイル置き場ではない</strong>。PDFもWordも置けない。契約書の保管はNextcloudに残した。</li>
<li><strong>何でも1箇所にある便利さを捨てる</strong>。サーバーが1つ増え、端末のアカウントも1つ増える。</li>
</ul>
<p>代わりに得たのは、速さ、攻撃される面の小ささ(PHPの大きな母屋とデータベースが無くなる)、そして<strong>バックアップがフォルダのコピーで済み、ソフトが壊れても中身はテキストで読める</strong>こと。この3つのために上の4つを捨てた。判断は人によって逆でいい。</p>
<h2 id="立てて流し込む">立てて流し込む</h2>
<p>構成は<code>docker compose</code>で1つ、<code>tomsquest/docker-radicale</code>(Radicale 3.8.1)。<code>127.0.0.1</code>にだけ口を開け、外から直接は触れないようにした。認証はhtpasswd(ユーザー名とパスワードの対応表ファイル)だけ。<strong>設定の書き方は版で変わるので公式ドキュメントのConfigurationの項を見てほしい。</strong></p>
<p>移行前にNextcloudから控えを取る。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 住所録</span>
</span></span><span class="line"><span class="cl">curl -u <span class="s2">&#34;&lt;ユーザー名&gt;:&lt;アプリパスワード&gt;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;https://drive.example.com/remote.php/dav/addressbooks/users/&lt;ユーザー名&gt;/contacts?export&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 予定表</span>
</span></span><span class="line"><span class="cl">curl -u <span class="s2">&#34;&lt;ユーザー名&gt;:&lt;アプリパスワード&gt;&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  <span class="s2">&#34;https://drive.example.com/remote.php/dav/calendars/&lt;ユーザー名&gt;/&lt;予定表名&gt;?export&#34;</span>
</span></span></code></pre></div><p>入れ物の一覧は同じURLに<code>-X PROPFIND -H &quot;Depth: 1&quot;</code>を付けて取る。流し込みはスクリプトを書いた。設計で効いた点が2つ。</p>
<ul>
<li><strong>ファイル名をデータの中のUID(1件ごとの識別子)にする</strong>。何度流し直しても同じファイルを上書きするだけで、件数が増えない。</li>
<li><strong>繰り返す予定の「例外日」(毎週の予定のうち1回だけ時間をずらした分)は同じUIDを持つので、1ファイルにまとめる</strong>。別ファイルにすると予定が二重になる。</li>
</ul>
<p>結果は575件・236件でNextcloud側と<strong>完全一致、失敗0件</strong>。</p>
<h3 id="-立てるときに踏んだ穴">🔴 立てるときに踏んだ穴</h3>
<ul>
<li><strong>コンテナの中のRadicaleは自分と違うユーザーで動く</strong>。自分の場合は数字のユーザーID 2999だった。パスワード表を<code>root</code>の持ち物にしたままだと<code>Permission denied</code>で起動しない。<strong>そのIDに所有者を変え、読み取りだけ許す</strong>のが正解。</li>
<li>🔴 <strong>「正常な応答コード」を3回続けて読み違えた</strong>。トップページの<code>GET</code>は<strong>302</strong>(転送)が正常で401ではない。予定表フォルダの<code>GET</code>は認証が通っていても<strong>403</strong>が仕様どおりで、壊れていない。<strong>正しい確認は<code>PROPFIND</code>を投げて207が返ること</strong>で、件数もこれで数える。</li>
</ul>
<h2 id="端末の繋ぎ直しに移行は無い">端末の繋ぎ直しに「移行」は無い</h2>
<p>ここが一番地味で、一番時間がかかる。<strong>サーバー間でデータを移しても、端末のアカウント設定は1台も自動で付いてこない。全部手で繋ぎ直す。</strong> Apple(Mac・iPhone)で詰まる点は1つだけで、はっきりしている。</p>
<ul>
<li>🔴 <strong>アカウント種別の「自動(Automatic)」では繋がらない</strong>。Appleは<code>.well-known/caldav</code>という決まった入口で「本当の場所はどこ?」と尋ねるが、Radicaleはここでトップページへ転送するだけで場所を教えない。</li>
<li><strong>必ず「手動(Manual)」を選ぶ</strong>。サーバーアドレスはホスト名(<code>cal.example.com</code>)、駄目なら自分のフォルダまでのフルパス(<code>https://cal.example.com/&lt;ユーザー名&gt;/</code>)を直接入れる。前の記事と同じ系統で、<strong>自動検出は当てにしない</strong>。</li>
</ul>
<p>AndroidとThunderbirdは今回つないでいないので手順を書かない。確かなのは、<strong>Androidの標準カレンダーにはCalDAVアカウントを足す項目が無く、別途CalDAVクライアントのアプリを入れて端末のカレンダーに同期させる形になる</strong>こと、<strong>ThunderbirdはCalDAVに標準対応している</strong>ことだけだ。</p>
<p>並行期間(両方に同じデータがある状態)の事故防止で効いたこと。</p>
<ul>
<li><strong>古い側の表示名を「OLD(使わない)」に変える</strong>。同じ名前が2つ並ぶと、どちらを編集しているか分からなくなる。</li>
<li>端末側で<strong>新規作成先の既定のカレンダーを新しい方に変える</strong>(Macは Calendar &gt; Settings &gt; General、iPhoneは 設定 &gt; アプリ &gt; カレンダー)。ここを変えないと、自分で作った予定だけ古い方に入り続ける。</li>
<li>古い側を消すのは、①手元に控えがある②新しい側の件数が一致している、の両方を実測してから。</li>
<li>🔴 <strong>Nextcloudの予定表は<code>204</code>が返っても消えていない</strong>。ごみ箱行き(論理削除)になり、一覧に出続けて中身も読める。完全に消すには削除要求に<code>X-NC-CalDAV-No-Trashbin: 1</code>を付ける。住所録はごみ箱が無く一発で消えるので、挙動が揃っていない。</li>
</ul>
<h2 id="-radicaleは重いreport問い合わせを同時に受けられない">🔴 Radicaleは重いREPORT問い合わせを同時に受けられない</h2>
<p><strong>Radicaleを選ぶ前に知っておくべき制約</strong>で、しかもほとんどどこにも書かれていない。自分は後から実測で踏んだ。</p>
<p>REPORTは、カレンダーアプリが「この日からこの日までに何が入っているか教えて」と尋ねるCalDAVの問い合わせだ。Radicaleはこれを受けると<strong>フォルダの中の全ファイルを毎回走査する</strong>。繰り返す予定は1ファイルにルールが1行入っているだけなので、「表示を1年先までに絞る」といった対策は効かない。<strong>遅さを決めているのは問い合わせの範囲ではなく、フォルダ内のファイル総数</strong>だ。</p>
<p>予定表が現在2,626件まで増えた結果、1回の範囲問い合わせに<strong>約4.7秒</strong>かかる。これ自体は待てる。問題は同時に投げたときだった。</p>
<ul>
<li>事前読み込みを<strong>直列3本から並列5本に変えたら、30秒で5本とも通信切れになり、取れた予定は0件</strong>になった。</li>
<li><strong>直列に戻したら、5期間228件が23.3秒で全部成功</strong>した(1本あたり約4.7秒)。</li>
</ul>
<p>つまり<strong>Radicaleは重いREPORTを並べて投げられると、速くなるどころか全部落ちる</strong>。「カレンダーアプリを2つ同時に開く」「月を素早く切り替える」「複数の範囲を先読みする自作ツール」のどれでも起こり得る。付き合い方は、どれも「並列化しない」方向になる。</p>
<ol>
<li><strong>1本ずつ流す列を作る</strong>。手前のサーバーに順番待ちの列を1本入れ、範囲問い合わせは必ず直列で流す。</li>
<li><strong>同じ範囲の同時要求は1本に相乗りさせる</strong>。2箇所から同じ月を聞かれたら投げるのは1回で、答えを2箇所に配る。画面の操作から来た要求だけ列の先頭に割り込ませると、裏の先読みが詰まっていても人は待たされない。</li>
<li><strong>一度取ったら捨てない</strong>。1件保存・削除するたびに全期間のキャッシュを丸ごと捨てて再取得していたのが最悪の形だった。<strong>変わった1件だけを手元で差し替える</strong>ようにしたら、保存のたびの5秒が消えた。</li>
<li><strong>範囲は広めに取って一度だけ聞く</strong>。細かく刻んで何度も聞くほうが遅い。</li>
</ol>
<p>⭐ 一般則。<strong>相手が1本ずつしか捌けない所へ、並べて投げてはいけない。速くする手は並列化ではなく、①同じものを二度聞かない ②要るものだけ聞く、の2つしかない。</strong></p>
<h2 id="時間帯は最初に片付ける">時間帯は最初に片付ける</h2>
<p>2人で共有するカレンダーを、互いに別の国にいる状態で使っている。<strong>時間帯(タイムゾーン)の扱いは後から足す機能ではなく、土台</strong>だった。救われたのは、保存の形が最初から正しかったことだ。<code>.ics</code>の中では開始時刻が<code>DTSTART;TZID=Asia/Singapore:...</code>のように**「どの地域の時計で何時か」の形で書かれている**ので、絶対の時刻として一意に決まる。Apple純正のカレンダーはこれを読む側の端末の時間帯で出し直してくれる。<strong>標準形式に素直に保存されていれば、時間帯は自動で解決する。</strong> 壊れたのは自作の画面のほうで、そこから学んだのは次の3つ。</p>
<ul>
<li><strong>変換は入口と出口の2箇所だけでやる</strong>。画面に出す直前と、入力を受け取った直後。途中の計算に混ぜると、どこが変換済みか分からなくなって必ず二重変換が起きる。</li>
<li><strong>「いまどの時間帯で見ているか」を常に画面に出す</strong>。Google・Apple・Thunderbirdのどれもこうしている。あとから読まれる文章に時刻を書くときも、必ず時間帯を添える(保存された文は読む人の時間帯に書き直せない)。</li>
<li><strong>時刻の変換は、画面で確かめる前に机上で全パターン流す</strong>。夏時間の切り替わりの前後、30分ずれる地域、日付をまたぐ西側の地域は、実機を1件ずつ見ても踏めない。</li>
</ul>
<h2 id="バックアップは単純になった">バックアップは単純になった</h2>
<p>データが素のファイルになったので、バックアップは<strong>フォルダを同期するだけ</strong>になった。既存の日次スクリプトに2行足して終わりで、初回送信は1,035ファイル・379KiBで32秒。ただし<strong>消されても戻せる形にしておくことは、データが素のファイルになっても別途必要</strong>で、そこは変わらない(<a href="/guides/backup-ransomware-resistant/">バックアップを「消されても戻せる」形にする</a>)。</p>
<h2 id="結果">結果</h2>
<p>575件・236件の移行は失敗0で完了し、MacとiPhoneから繋がっている。サーバー上にあるのは1,000枚ほどのテキストファイルで、<code>cat</code>で開けば中身が読める。</p>
<p>ファイル置き場のNextcloudは据え置いた。素のファイルを普通のフォルダ構造で保存しているという一点では、調べた移行先候補よりNextcloudのほうが上だったからだ。<strong>目的が「データを読める形に保つ」なのに、手段がそこを後退させるなら却下する。</strong></p>
<p><strong>更新履歴</strong> — 2026-10-05: 初版</p>
]]></content:encoded></item></channel></rss>