Say which path and user cannot open the database

sql.Open is lazy, so a permission problem surfaced from whichever query ran
first: "create schema_migrations: unable to open database file (14)", which
names neither the file nor the reason. Ping on open and report the path and
the effective uid and gid instead.

The cause in practice is a bind-mounted ./data that Docker created as root
while the container runs as FOODSTER_UID. Documented in the README.
This commit is contained in:
Esa Kataja
2026-09-05 20:23:44 +03:00
parent eb95bd0a03
commit 813e82ef5c
2 changed files with 21 additions and 0 deletions
+10
View File
@@ -181,6 +181,16 @@ That directory must exist and be owned by the user compose runs as — `make up`
creates it, and `FOODSTER_UID`/`FOODSTER_GID` in `.env` tell the container who
that is. Get them from `id -u` and `id -g`.
If the app exits with `cannot open /data/foodster.db ... unable to open
database file (14)`, the ownership does not match. Docker creates a missing
bind-mount directory as root, and the container is not root:
```sh
ls -ldn data # whose is it?
sudo chown -R 1000:1000 data # match FOODSTER_UID / FOODSTER_GID
docker compose restart
```
## Security
Access is a single shared password over HTTP Basic — no accounts, no
+11
View File
@@ -128,6 +128,17 @@ func openDB(path string) (*sql.DB, error) {
// sidesteps SQLITE_BUSY entirely. Raise it if reads ever contend.
db.SetMaxOpenConns(1)
// sql.Open is lazy, so without this the first failure surfaces from
// whatever query ran first and says nothing useful. The usual cause is a
// bind-mounted directory owned by a different user than the container
// runs as, so name the path and the uid.
if err := db.Ping(); err != nil {
db.Close()
return nil, fmt.Errorf(
"cannot open %s as uid %d gid %d: %w (is that directory writable by this user?)",
path, os.Getuid(), os.Getgid(), err)
}
if err := migrate(db); err != nil {
db.Close()
return nil, err