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 of the fix

  1. The admin password did not work, even after reset attempts.
  2. Grafana ran manually, but it did not run reliably as a user service through brew services. The service needed to start at boot without a desktop login.
  3. We fixed both problems:
  • Reset the password against the actual Brew paths and database that the running server uses.
  • Replace the GUI-bound LaunchAgent approach with a system LaunchDaemon at /Library/LaunchDaemons/... that runs at boot as your user.

Caveats

The password reset reported success but used the wrong database

You can point Grafana 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 with Grafana’s default data directory under the install tree. Some attempts did not override the paths. These attempts could target:

  • /usr/local/opt/grafana/share/grafana/data

The CLI reported success. But the running server still used the original database at /usr/local/var/lib/grafana. So the login still failed.

Grafana v12 changed the CLI behavior, and the 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 used --configOverrides for the CLI instead of --packaging=brew.

brew services uses LaunchAgents in the GUI login domain

Homebrew services normally create a LaunchAgent at:

  • ~/Library/LaunchAgents/homebrew.mxcl.grafana.plist

LaunchAgents are tied to a user session. They can fail in these cases:

  1. Nobody is logged into the GUI.
  2. The system reboots or respawns the agent.
  3. Permissions or environment variables differ from an interactive shell.

The goal was to start Grafana at boot with no GUI login. That requires a LaunchDaemon in the system domain:

  • /Library/LaunchDaemons/...

Root and Brew do not mix

brew as root produced this error:

  • Error: Need to download ... but cannot as root!

So the final boot-time solution does not use brew at all.


Evidence: confirm which DB/path Grafana is actually using

This log confirms the running server points at the Brew var paths:

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/grafana
  • Connecting to DB dbtype=sqlite3

Fix 1: Reset the admin password correctly (Grafana v12 and 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 --homepath correctly)
  • 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 with no desktop login, with a LaunchDaemon

Why a system LaunchDaemon instead of the 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 you repeat this step, unload the daemon first. This step is 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 = system
  • state = running
  • username = iciadmin
  • working directory = /usr/local/opt/grafana/share/grafana
  • arguments = ... (shows grafana 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)

The LaunchDaemon does not load

  1. Check the plist ownership and permissions. They must be root:wheel and 0644.
  2. Check the logs:
tail -n 200 /usr/local/var/log/grafana/launchd.err
tail -n 200 /usr/local/var/log/grafana/grafana.log

Grafana runs but the login fails again

  1. Verify the server uses the expected database:
grep -E 'Path Data|Connecting to DB' /usr/local/var/log/grafana/grafana.log | tail -n 5
  1. Run the password reset again with the same overrides used here.

brew services still shows error 78

That is the old user LaunchAgent path. It is not needed once the LaunchDaemon is in place. The final setup does not rely on brew services for boot-time behavior.


Final outcome

  • We reset the admin password in the correct SQLite database used by the running Brew instance.
  • Grafana now starts at system boot as user iciadmin, with no GUI login, through a custom LaunchDaemon.