<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[SQLite backups: restore never applies the dump (docker run without -i)]]></title><description><![CDATA[<p dir="auto">I maintain an unofficial package for <a href="https://peergos.org" target="_blank" rel="noopener noreferrer nofollow ugc">Peergos</a> (<a href="https://github.com/tripod0429/peergos-app" target="_blank" rel="noopener noreferrer nofollow ugc">tripod0429/peergos-app</a>) that lists its databases under <code>localstorage.sqlite.paths</code>. While testing backup and restore, I found that the SQLite dumps Cloudron takes during a backup are never applied on restore. A restored app gets the copies of its live database files that the backup upload made while the app was running.</p>
<h3>What happens</h3>
<p dir="auto">From box master <a href="https://git.cloudron.io/platform/box/-/tree/21b1abd16c84df6eac3e19cf047cdf2ee9276120" target="_blank" rel="noopener noreferrer nofollow ugc">21b1abd</a>:</p>
<ol>
<li><code>backupSqlite</code> dumps each listed database with <code>sqlite3 &lt;db&gt; .dump</code> before the upload starts <a href="https://git.cloudron.io/platform/box/-/blob/21b1abd16c84df6eac3e19cf047cdf2ee9276120/src/services.js#L331-353" target="_blank" rel="noopener noreferrer nofollow ugc">services.js#L331-353</a>. That part works as intended.</li>
<li>The upload then copies the whole data directory, including the live database files. The listed paths aren't excluded.</li>
<li>On restore, <code>restoreSqlite</code> replays each dump like this <a href="https://git.cloudron.io/platform/box/-/blob/21b1abd16c84df6eac3e19cf047cdf2ee9276120/src/services.js#L363-382" target="_blank" rel="noopener noreferrer nofollow ugc">services.js#L363-382</a>:<pre><code>docker run --rm ... &lt;app image&gt; sqlite3 &lt;db&gt; &lt; &lt;dump&gt;
</code></pre>
There's no <code>-i</code>, so Docker doesn't pass stdin to the container. <code>sqlite3</code> reads nothing and exits 0, and the restore carries on as if it worked.</li>
</ol>
<h3>Quick check</h3>
<pre><code>printf 'CREATE TABLE t(x); INSERT INTO t VALUES(42); SELECT x FROM t;\n' | docker run --rm cloudron/base:6.0.0 sqlite3
printf 'CREATE TABLE t(x); INSERT INTO t VALUES(42); SELECT x FROM t;\n' | docker run --rm -i cloudron/base:6.0.0 sqlite3
</code></pre>
<p dir="auto">On my Cloudron server (Docker 29.6.2), the first command prints nothing and exits 0. The second prints <code>42</code>. Podman 5.8.7 behaves the same way.</p>
<h3>Adding <code>-i</code> alone isn't enough</h3>
<p dir="auto">The live database is already in the restored data, so every <code>CREATE TABLE</code> in the dump fails and <code>sqlite3</code> exits 1. Cloudron's shell helper turns any non-zero exit into an error (<a href="https://git.cloudron.io/platform/box/-/blob/21b1abd16c84df6eac3e19cf047cdf2ee9276120/src/shell.js#L58-84" target="_blank" rel="noopener noreferrer nofollow ugc">shell.js#L58-84</a>), so with only <code>-i</code> added, every restore of an app with <code>sqlite.paths</code> would fail. If that error were ignored instead, tables without a unique key would get duplicate rows:</p>
<pre><code>$ sqlite3 live.db "CREATE TABLE notes (body text); INSERT INTO notes VALUES ('one');"
$ sqlite3 live.db .dump &gt; dump.sql
$ sqlite3 live.db &lt; dump.sql; echo "exit $?"
Parse error near line 3: table notes already exists
exit 1
$ sqlite3 live.db "select count(*) from notes;"
2
</code></pre>
<h3>Why it matters</h3>
<p dir="auto">For most apps a live copy is close enough, which is probably why this hasn't come up. But:</p>
<ul>
<li>A database being written while it's copied can come back corrupted, which is what the dumps are there to prevent.</li>
<li>In WAL mode, the database and its <code>-wal</code> file are copied at different moments.</li>
<li>Apps that keep files next to their database can get rows pointing to files the upload had already passed.</li>
</ul>
<h3>Suggested fix</h3>
<ul>
<li>In <code>restoreSqlite</code>, pass <code>-i</code> to <code>docker run</code> and replay each dump into a new file instead of the restored copy. Once the replay succeeds, move the new file over the database and delete any <code>-journal</code>, <code>-wal</code> or <code>-shm</code> file left from the live copy.</li>
<li>Optionally, leave the listed paths out of the upload, since the dumps replace them. That also avoids uploading every database twice.</li>
</ul>
]]></description><link>https://forum.cloudron.io/topic/16125/sqlite-backups-restore-never-applies-the-dump-docker-run-without-i</link><generator>RSS for Node</generator><lastBuildDate>Sun, 18 Oct 2026 23:02:01 GMT</lastBuildDate><atom:link href="https://forum.cloudron.io/topic/16125.rss" rel="self" type="application/rss+xml"/><pubDate>Thu, 15 Oct 2026 08:23:20 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to SQLite backups: restore never applies the dump (docker run without -i) on Sat, 17 Oct 2026 08:03:26 GMT]]></title><description><![CDATA[<p dir="auto">Really good catch! I have fixed this for the next release. Thanks for reporting.</p>
]]></description><link>https://forum.cloudron.io/post/131129</link><guid isPermaLink="true">https://forum.cloudron.io/post/131129</guid><dc:creator><![CDATA[girish]]></dc:creator><pubDate>Sat, 17 Oct 2026 08:03:26 GMT</pubDate></item></channel></rss>