Fixing a grafana instance on macOS
Date: 2026-02-08
Host: server (macOS Monterey; Homebrew prefix: /usr/local)
Grafana install: Homebrew (/usr/local/opt/grafana)
Grafana data: /usr/local/var/lib/grafana
Grafana (Homebrew on macOS Monterey) & Password Reset + Boot-Time Startup (No GUI Login)
Summary (what was fixed)
- Admin login/password wouldn’t work even after “reset” attempts.
- Grafana would run manually, but would not reliably run as a user service via
brew servicesand you needed it to start at boot without any desktop login. - fixed both by:
- Resetting the password against the actual Brew paths + DB that the running server uses.
- Replacing the GUI-bound LaunchAgent approach with a system LaunchDaemon at
/Library/LaunchDaemons/...that runs at boot as your user.
Caveats
“Password reset succeeded” was misleading because I was hitting the wrong DB
Grafana can be pointed at different data directories. In this setup, the running Brew Grafana server uses:
- Data dir:
/usr/local/var/lib/grafana - SQLite DB:
/usr/local/var/lib/grafana/grafana.db - Logs:
/usr/local/var/log/grafana - Plugins:
/usr/local/var/lib/grafana/plugins - Homepath (defaults.ini lives here):
/usr/local/opt/grafana/share/grafana
Earlier attempts reset the password using Grafana’s default data directory under the install tree (or via an invocation that did not override paths), which can target:
/usr/local/opt/grafana/share/grafana/data
Result: CLI reported success, but the actual running server continued to use the original DB under /usr/local/var/lib/grafana, so login still failed.
Grafana v12 CLI behavior changed (and flags differ between server and cli)
On Grafana v12, the correct CLI is:
- ✅
/usr/local/opt/grafana/bin/grafana cli ...
But the --packaging=brew flag:
- ✅ valid for
grafana server - ❌ NOT valid for
grafana cli(saw:flag provided but not defined: -packaging)
So I had to use --configOverrides (for the CLI) instead of --packaging=brew.
brew services uses LaunchAgents (GUI login domain)
Homebrew services normally create a LaunchAgent in:
~/Library/LaunchAgents/homebrew.mxcl.grafana.plist
LaunchAgents are tied to a user session and can behave poorly when:
- nobody is logged into the GUI,
- the agent gets booted out / respawned,
- or permissions/environment differ from interactive shells.
The goal was: start at boot, no GUI login That requires a LaunchDaemon in the system domain:
/Library/LaunchDaemons/...
Root vs Brew
Running brew as root caused:
Error: Need to download ... but cannot as root!
So I avoided brew entirely for the final boot-time solution.
Evidence: confirm which DB/path Grafana is actually using
Confirmed the running server points at the Brew var paths by reading Grafana’s own log:
grep -E 'Path Data|Connecting to DB|dbtype=sqlite3' /usr/local/var/log/grafana/grafana.log | tail -n 20
Expected (and observed) patterns:
Path Data path=/usr/local/var/lib/grafanaConnecting to DB dbtype=sqlite3
Fix 1 — Reset the admin password correctly (Grafana v12 + Homebrew paths)
1.1 Verify defaults.ini exists under the homepath
Grafana needs conf/defaults.ini under the homepath you pass:
ls -la /usr/local/opt/grafana/share/grafana/conf/defaults.ini
1.2 Run the password reset against the correct homepath + config + overrides
Critical details:
- Run from the homepath directory (or set
--homepathcorrectly) - Use your Brew config:
/usr/local/etc/grafana/grafana.ini - Override the default paths so the CLI hits
/usr/local/var/lib/grafana/grafana.db
cd /usr/local/opt/grafana/share/grafana
/usr/local/opt/grafana/bin/grafana cli --homepath /usr/local/opt/grafana/share/grafana --config /usr/local/etc/grafana/grafana.ini --configOverrides "cfg:default.paths.data=/usr/local/var/lib/grafana cfg:default.paths.logs=/usr/local/var/log/grafana cfg:default.paths.plugins=/usr/local/var/lib/grafana/plugins" admin reset-admin-password 'fuckface'
Expected success message:
Admin password changed successfully ✅
1.3 Confirm the admin user in the correct DB
sqlite3 /usr/local/var/lib/grafana/grafana.db "select id,login,email,is_admin,is_disabled from user order by id limit 20;"
Expected output (as observed):
1|admin|admin@localhost|1|0
1.4 Confirm Grafana is listening and responding
curl -sSI http://127.0.0.1:3000/login | head
netstat -anv | grep '\.3000 ' | grep LISTEN
Fix 2 — Start Grafana at boot (no desktop login) using a LaunchDaemon
Why LaunchDaemon (system) instead of brew services (LaunchAgent)
- LaunchAgent: user GUI login domain; unreliable without a login session.
- LaunchDaemon: system boot domain; runs at startup even with no GUI login.
We created our own LaunchDaemon plist:
- Path:
/Library/LaunchDaemons/com.grafana.server.plist - Runs as user:
iciadmin(not root)
The LaunchDaemon plist
File: /Library/LaunchDaemons/com.grafana.server.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.grafana.server</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<!-- Run Grafana as a normal user -->
<key>UserName</key>
<string>iciadmin</string>
<key>GroupName</key>
<string>admin</string>
<!-- Must be the Grafana homepath (contains conf/defaults.ini) -->
<key>WorkingDirectory</key>
<string>/usr/local/opt/grafana/share/grafana</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/opt/grafana/bin/grafana</string>
<string>server</string>
<string>--config</string>
<string>/usr/local/etc/grafana/grafana.ini</string>
<string>--homepath</string>
<string>/usr/local/opt/grafana/share/grafana</string>
<!-- Valid for grafana server -->
<string>--packaging=brew</string>
<!-- Force Homebrew paths -->
<string>cfg:default.paths.data=/usr/local/var/lib/grafana</string>
<string>cfg:default.paths.logs=/usr/local/var/log/grafana</string>
<string>cfg:default.paths.plugins=/usr/local/var/lib/grafana/plugins</string>
</array>
<!-- launchd logs -->
<key>StandardOutPath</key>
<string>/usr/local/var/log/grafana/launchd.out</string>
<key>StandardErrorPath</key>
<string>/usr/local/var/log/grafana/launchd.err</string>
</dict>
</plist>
Commands used to create and enable the LaunchDaemon
2.1 Create the plist (as root via doas)
doas tee /Library/LaunchDaemons/com.grafana.server.plist >/dev/null <<'PLIST'
[...XML CONTENT AS SHOWN ABOVE...]
PLIST
2.2 Set required ownership and permissions
LaunchDaemons must be owned by root:wheel and mode 0644:
doas chown root:wheel /Library/LaunchDaemons/com.grafana.server.plist
doas chmod 644 /Library/LaunchDaemons/com.grafana.server.plist
2.3 Ensure the log directory exists and is writable by the Grafana user
doas mkdir -p /usr/local/var/log/grafana
doas chown -R iciadmin:admin /usr/local/var/log/grafana
doas chmod 775 /usr/local/var/log/grafana
2.4 Fix the SQLite DB permission warning
Grafana warned that the DB file had broader permissions than expected. We corrected it:
doas chmod 640 /usr/local/var/lib/grafana/grafana.db
doas chown iciadmin:admin /usr/local/var/lib/grafana/grafana.db
2.5 Load and enable the daemon (system domain)
If iterating, unload first (safe):
doas launchctl bootout system /Library/LaunchDaemons/com.grafana.server.plist 2>/dev/null || true
Then load + enable:
doas launchctl bootstrap system /Library/LaunchDaemons/com.grafana.server.plist
doas launchctl enable system/com.grafana.server
Verification that it worked
3.1 Confirm launchd sees it and it is running
doas launchctl print system/com.grafana.server | sed -n '1,200p'
Expected highlights:
domain = systemstate = runningusername = iciadminworking directory = /usr/local/opt/grafana/share/grafanaarguments = ...(showsgrafana server ... cfg:default.paths.*)
3.2 Confirm Grafana responds locally
curl -sSI http://127.0.0.1:3000/login | head
Expected:
HTTP/1.1 200 OK
Where everything lives (paths)
Homebrew/Grafana
- Binary:
/usr/local/opt/grafana/bin/grafana - Homepath:
/usr/local/opt/grafana/share/grafana - Config:
/usr/local/etc/grafana/grafana.ini
Data (critical)
- Data dir:
/usr/local/var/lib/grafana - DB:
/usr/local/var/lib/grafana/grafana.db - Plugins:
/usr/local/var/lib/grafana/plugins
Logs
- Grafana log:
/usr/local/var/log/grafana/grafana.log - launchd stdout:
/usr/local/var/log/grafana/launchd.out - launchd stderr:
/usr/local/var/log/grafana/launchd.err
Boot-time startup definition
- LaunchDaemon plist:
/Library/LaunchDaemons/com.grafana.server.plist
Common failure modes (quick triage)
LaunchDaemon won’t load
- Check plist ownership/permissions:
- must be
root:wheeland0644
- must be
- Check logs:
tail -n 200 /usr/local/var/log/grafana/launchd.err tail -n 200 /usr/local/var/log/grafana/grafana.log
Grafana runs but login fails again
- Verify the server is using the expected DB:
grep -E 'Path Data|Connecting to DB' /usr/local/var/log/grafana/grafana.log | tail -n 5 - Re-run the password reset with the same overrides used here.
brew services still shows error 78
That is the old user LaunchAgent path. It’s not needed once the LaunchDaemon is in place.
(We intentionally stopped relying on brew services for boot-time behavior.)
Final outcome
- Admin password was reset in the correct SQLite DB used by the running Brew instance.
- Grafana now starts at system boot as user
iciadminwithout any GUI login, via a custom LaunchDaemon.