Diagnosing Forgejo Startup Failures Caused by SQLite Locks and Migrations
Forgejo won't start and the logs say "database is locked" or a migration never finishes? A diagnostic walkthrough for SQLite-backed instances: ordered checks, fixes per finding, and when to restore from backup.
26 Feb 2026, 22:14 UTC

Your Forgejo instance refuses to start, or restarts in a loop, and the web UI never comes up. The logs mention database is locked, a migration that never finishes, or a timeout waiting for the database. On small self-hosted deployments that use the built-in SQLite backend, this is almost always a lock or migration problem, not an application bug — and it is usually recoverable without touching the database file itself.
This guide assumes a single-node Forgejo deployment using SQLite (the default DB_TYPE = sqlite3 in app.ini), typical for personal or small-team instances. Exact migration behavior varies between Forgejo releases, so treat version-specific details as approximate and verify against your own logs.
Recognizing the condition
The recognizable pattern is: Forgejo fails to start or is restarted repeatedly by your service manager; the web UI is unreachable; and the log shows database-level errors rather than HTTP or application errors. Typical log lines include database is locked, database table is locked, migration messages that start but never log a completion, or timeouts while acquiring the database.
This often appears after an unclean shutdown (power loss, OOM kill, kill -9), an upgrade that triggered a schema migration, or accidentally running two Forgejo processes against the same data directory.
Symptom-to-cause table
| Symptom in logs | Likely cause |
|---|---|
database is locked immediately at startup | A second Forgejo process holds the file, or a stale lock from a crashed process |
| Migration start logged, no completion, startup hangs | Long-running schema migration on a large or busy SQLite file |
permission denied opening the database | Ownership/permission mismatch after a data-directory move or container restart |
I/O errors or disk I/O error | Full filesystem or failing storage under the data directory |
Ordered checks
Run these on the Forgejo host, with a shell user that can read the data directory (root or the Forgejo runtime user via sudo).
- Confirm only one Forgejo process exists. Run
ps aux | grep forgejoand, more precisely, check which processes hold the database file open:sudo lsof /var/lib/forgejo/data/forgejo.db(adjust the path to your configured SQLite file). If two Forgejo binaries appear — for example a leftover manual start plus a systemd service — that is your lock source. - Confirm the backend and file location. In
app.ini(commonly/etc/forgejo/app.inior inside the container at/data/gitea/conf/app.ini), check the[database]section:DB_TYPE = sqlite3and thePATHvalue. Verify exactly one database file exists there. - Check ownership and permissions.
ls -lon the database file and its directory. The runtime user (oftengitorforgejo) needs read/write on both. A common failure is restoring or moving the directory as root, leaving files owned by root. - Review the log for migration markers. Look for timestamped lines indicating a migration started and whether a matching completion line exists. If a migration began minutes ago on a large database and the process is alive and doing I/O, it may simply need time.
- Check disk space and filesystem health.
df -hon the data directory's filesystem. SQLite cannot write to a full filesystem, and interrupted writes are a corruption risk. - Test read-only access. With Forgejo stopped, run
sqlite3 /path/to/forgejo.db "PRAGMA integrity_check;"(requires thesqlite3CLI; run read-only, make no changes). A cleanokconfirms basic accessibility. Do not run any write or repair statements.
Fixes tied to findings
- Duplicate processes: stop every Forgejo instance (
sudo systemctl stop forgejo, plus kill any stray manual process), verifylsofshows nothing holding the file, then start a single instance. - Stale lock after a crash: SQLite normally cleans up its rollback journal or WAL sidecar files (
forgejo.db-journal,forgejo.db-wal) automatically on next open. Ensure no process holds the file and start Forgejo cleanly once. Do not delete these files manually unless you have a verified backup — deleting them can discard uncommitted transactions and worsen corruption. - Permission mismatch:
sudo chown -R git:git /var/lib/forgejo/data(use your actual runtime user and path), then restart. - Migration in progress: let it finish. Interrupting a migration mid-write is how databases get corrupted. Watch the log until a completion marker appears.
- Disk full: free space, then restart and watch startup in the foreground once to confirm clean progress.
Never hand-edit the SQLite file or run manual SQL repairs as a first response. Take a file-level backup first: with Forgejo stopped, copy the database file and the whole data directory somewhere safe.
Verifying the fix
Start Forgejo once in the foreground (run the binary directly as the runtime user, or docker/podman run attached) and watch the output. You want to see migrations complete and the HTTP listener start without lock errors. Then stop it and return to your normal service manager. Finally, log into the web UI and spot-check a few repositories and issues to confirm the data looks intact.
When to escalate
Escalate to a restore from a known-good backup, and to community support with sanitized logs, when any of these hold:
- Lock errors persist after you have verified a single process and correct permissions.
- The log shows corruption indicators (for example
database disk image is malformed, orPRAGMA integrity_checkreports anything other thanok). - A migration fails at the same step across repeated restarts.
- You see data-loss symptoms such as missing repositories or issues.
If lock contention recurs under normal load — several users pushing and browsing concurrently — that is a known limitation of the SQLite backend rather than a fault. The durable fix is migrating to PostgreSQL or MySQL, which Forgejo supports for higher-concurrency deployments.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.