On this page
lokal setup runs once per Mac. It’s the only part of installing lokal that changes system settings: it trusts
lokal’s certificate, sends .lokal lookups to lokal and starts the background service. It shows its plan and asks before it changes
anything, and it takes about 10 seconds.
This page describes macOS. On Linux (a preview), setup takes the same steps with Linux’s own tools: see On Linux.
Setup and lokal uninstall are the only commands that ever ask for your password. Creating projects and adding,
changing or removing services never do.
$ lokal setupRun it as yourself, not with sudo. lokal asks for your password itself, for the steps that need it, and refuses
to run setup as root.
What setup does#
On a fresh Mac, setup:
- Creates lokal’s certificate authority in
~/Library/Application Support/lokal/ca/, as you. It can only sign names under.lokaland.local(lokal’s TLD before 0.6.0), and its private key never leaves that folder. - Trusts it in the macOS System keychain, so browsers and macOS tools accept lokal’s certificates.
- Sends
.lokallookups to lokal by installing/etc/resolver/lokal. This file tells macOS to ask lokal’s built-in DNS server (127.0.0.1:53535) about*.lokalnames. - Installs the background service, a LaunchAgent (
dev.lokal.daemon) that runs as you. launchd holds ports 443 and 80 on127.0.0.1and::1for it, so it never needs root, and nothing is reachable from your network. - Runs a self-test. It publishes a throwaway
.lokalname, resolves it, and fetches it over HTTPS verified by macOS.
If Firefox is installed, setup also configures it to trust lokal (see Firefox below). Setup also
writes the certificate bundle that lokal env uses. When it finishes, it prints the
next commands to run:
You're all set. Next:
lokal project create myproject
lokal service add api 4000
Tip: run 'eval "$(lokal env)"' so Node and Python trust lokal too.The password prompts#
Steps 2 and 3 are the only ones that need your password. Setup prints these commands before it runs them, and
they are the only commands it runs with sudo (/Users/you stands for your home folder):
sudo /usr/bin/security add-trusted-cert -d -r trustRoot -p ssl -k /Library/Keychains/System.keychain "/Users/you/Library/Application Support/lokal/ca/root.pem"
sudo /bin/mkdir -p /etc/resolver
sudo /usr/bin/install -m 0644 "/Users/you/Library/Application Support/lokal/resolver/lokal" /etc/resolver/lokallokal runs each one through sudo with an argument vector. It never runs a shell and never builds a command line
from strings. lokal’s only other privileged commands undo these: lokal uninstall runs them, and so does setup
when it replaces an outdated CA.
You type your password once in Terminal for sudo. macOS then shows its own dialog and asks for it again, because it confirms every change to certificate trust settings separately. On Linux there’s no second dialog: sudo asks once.
After setup, nothing runs as root. lokal asks for your password again only in lokal uninstall, or in
lokal setup if it has to repair one of these two steps. The security model
lists every file and setting setup changes, and what undoes each one.
Preview the plan with --dry-run#
lokal setup --dry-run prints the plan and the exact privileged commands, then exits without changing anything.
On a fresh Mac it looks like this:
$ lokal setup --dry-run
Welcome to lokal. This one-time setup makes https://<anything>.lokal work on this Mac.
1. Create lokal's certificate authority
Only valid for .lokal and .local names. Its key stays in your user folder.
2. Trust it in the macOS System keychain ← needs your password (once)
3. Send .lokal lookups to lokal (/etc/resolver/lokal) ← same password
You type your password once in Terminal (sudo); macOS then shows its own dialog for the keychain.
4. Install the background service (LaunchAgent, runs as you)
macOS will hold ports 443 and 80 (and DNS on 53535) on 127.0.0.1 for it. Nothing is reachable from your network.
5. Run a self-test
Privileged commands (the only ones; run with sudo):
sudo /usr/bin/security add-trusted-cert -d -r trustRoot -p ssl -k /Library/Keychains/System.keychain "/Users/you/Library/Application Support/lokal/ca/root.pem"
sudo /bin/mkdir -p /etc/resolver
sudo /usr/bin/install -m 0644 "/Users/you/Library/Application Support/lokal/resolver/lokal" /etc/resolver/lokal
Dry run: nothing was changed.Steps that are already in place are marked (already done). If nothing privileged is missing, the dry run says
No privileged command is needed.
Skip the confirmation with --yes#
Setup asks Continue? [Y/n] before it changes anything. lokal setup --yes (or -y) skips that question. It
doesn’t skip the password prompts: sudo still needs a terminal to ask for your password.
If setup needs sudo but can’t ask for a password, it stops before it changes anything and exits with code 4. That
happens when no terminal is attached, for example when a script or a coding agent runs it. Run lokal setup
yourself in a Terminal window. A coding agent should never run setup. It should ask you to.
Running setup again#
Setup is safe to re-run, and running it again is the repair path. It checks each step, fixes whatever is missing, and asks for your password only if one of the privileged steps is missing. If everything is already in place, it says so and changes nothing:
Everything was already set up; nothing to change.If lokal doctor reports a problem with the CA’s trust or the .lokal resolver, lokal setup is the fix. If the
existing CA doesn’t have exactly the name constraints this version expects (lokal doctor warns about this as
ca.scope), setup replaces it. The old CA is untrusted and removed in the same password prompt.
Firefox#
Firefox keeps its own certificate store. If Firefox is installed, setup adds this line to user.js in each
Firefox profile:
user_pref("security.enterprise_roots.enabled", true); // added by lokalThat preference makes Firefox trust every root CA that a user or administrator added to the macOS keychain, not
only lokal’s. If you don’t want that, delete the line. lokal then won’t work in Firefox, but it keeps working
everywhere else. lokal uninstall removes exactly that line.
If you create a Firefox profile after setup, run lokal setup again to configure it.
On Linux, setup doesn’t use this preference. It adds lokal’s CA to Firefox’s and Chrome’s own certificate databases instead: see Browsers on Linux.
If setup fails#
Start with lokal doctor. It checks every layer and says how to fix what’s broken. If the self-test fails, setup
names the layer that failed (DNS, connect, TLS and so on) and prints a hint. The background service’s log usually
has the details:
$ lokal doctor
$ lokal logs -n 200Common setup problems:
run 'lokal setup' as yourself, not with sudo. Run it without thesudoprefix.- Setup asks for your password twice. That’s expected on macOS: once for sudo in Terminal, once in the macOS dialog. On Linux it asks once.
- Setup fails with exit code 4. No terminal was attached, or
LOKAL_FORBID_SUDO=1is set. Runlokal setupyourself in Terminal. - Port 443 or 80 is already in use. Another program held the port when lokal’s background service started.
Setup names it and stops if it’s port 443 (port 80 alone is a warning). Stop that program or move it to another
port, then run
lokal restart.
Troubleshooting covers these and more.
On Linux#
Linux is a preview. Setup there needs systemd 258 or later with systemd-resolved running, such as Ubuntu 26.04 or Fedora 43; Linux (preview) lists everything it needs.
What setup does on Linux#
Setup creates the CA the same way, as you, in ~/.local/share/lokal/ca/. The other steps use Linux’s own tools:
- Trust. Setup copies the CA into the system certificate store as
lokal-<sha1>.crtand runs the distribution’s update tool, which rebuilds the certificate bundle that curl, OpenSSL and many other programs read. On Debian and Ubuntu that’s/usr/local/share/ca-certificatesandupdate-ca-certificates; on Fedora and Arch, the p11-kit anchors folder andupdate-ca-trust. Setup picks by which tool is installed. - Browsers. Chrome, Chromium and Firefox don’t read the system store, so setup also adds the CA to their own certificate databases, as you. See Browsers on Linux.
.lokallookups. Setup installs a systemd-resolved DNS delegate,/etc/systemd/dns-delegate.d/lokal.dns-delegate, which sends every*.lokallookup to lokal’s DNS server on127.0.0.1:53535. Then it restarts systemd-resolved, which reads delegates when it starts.- The background service. Setup installs systemd user units in
~/.config/systemd/user/:dev.lokal.daemon.service, which runs lokal as you, and three socket units that hold ports 443 and 80 on127.0.0.1and::1, and DNS on127.0.0.1:53535, for it. Nothing is reachable from your network. - Ports 443 and 80. A user’s service can’t bind ports below
net.ipv4.ip_unprivileged_port_start, which is 1024 by default. Setup lowers it to 80, with/etc/sysctl.d/50-lokal.conffor later boots andsysctl -wfor now. This lets every local user bind ports 80 to 1023, not only lokal. If the setting is already 80 or lower, setup skips this step.lokal uninstallremoves the file and sets the kernel’s default, 1024, back.
The password prompt on Linux#
The trust, resolver and port steps need root. Setup runs them as one batch, so you type your password once, for
sudo, and there’s no second dialog. It prints the commands before it runs them. On a fresh Ubuntu 26.04 machine,
lokal setup --dry-run shows (/home/you stands for your home folder):
$ lokal setup --dry-run
Welcome to lokal. This one-time setup makes https://<anything>.lokal work on this computer.
1. Create lokal's certificate authority
Only valid for .lokal and .local names. Its key stays in your user folder.
2. Trust it in the system certificate store ← needs your password (once)
3. Send .lokal lookups to lokal (/etc/systemd/dns-delegate.d/lokal.dns-delegate) ← same password
You type your password once (sudo).
4. Install the background service (systemd user units, runs as you)
systemd will hold ports 443 and 80 (and DNS on 53535) on 127.0.0.1 for it. Nothing is reachable from your network.
Ports below 1024 need one system setting: user services may bind ports 80 and up ← same password
5. Run a self-test
Privileged commands (the only ones; run with sudo):
sudo /usr/bin/install -m 0644 /home/you/.local/share/lokal/ca/root.pem /usr/local/share/ca-certificates/lokal-<sha1>.crt
sudo /usr/sbin/update-ca-certificates
sudo /usr/bin/install -D -m 0644 /home/you/.local/share/lokal/resolver/lokal /etc/systemd/dns-delegate.d/lokal.dns-delegate
sudo /usr/bin/systemctl restart systemd-resolved
sudo /usr/bin/install -m 0644 /home/you/.local/share/lokal/resolver/ports.conf /etc/sysctl.d/50-lokal.conf
sudo /usr/sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=80
Dry run: nothing was changed.On Fedora, the first two commands install the CA into /etc/pki/ca-trust/source/anchors/ and run
update-ca-trust instead. The security model lists every file these
commands change, and what undoes each one.
Browsers on Linux#
Chrome, Chromium and Firefox on Linux trust only their own certificate databases (NSS). After the password step,
setup adds lokal’s CA to each one it finds, with certutil, as you and never as root: ~/.pki/nssdb for Chrome
and Chromium, snap Chromium’s database, and every Firefox profile, including the snap and Flatpak ones. The CA is
trusted there for HTTPS servers only. If Chrome is installed but hasn’t run yet, setup creates its database. Each
database gets a line:
✓ Browser certificates ~/.pki/nssdb: trusted lokal's CAcertutil comes from libnss3-tools on Debian and Ubuntu, and nss-tools on Fedora. The .deb and .rpm
recommend it. Without it, setup still finishes, but if Chrome, Chromium or Firefox is installed, it warns:
! Browsers: certutil is missing, so browsers don't trust lokal yet: install libnss3-tools (Debian, Ubuntu) or nss-tools (Fedora), then run 'lokal setup' againIf you create a Firefox profile after setup, run lokal setup again. lokal doctor checks every database (in its
firefox check), and lokal uninstall removes lokal’s CA from each one.
When setup stops on Linux#
Before it changes anything, setup checks that each step can work on this machine. If one can’t, it stops with a
message that starts lokal can't be set up here yet:, and nothing is changed. lokal setup --dry-run prints the
plan, then the same message, and exits 1. The reasons:
systemd-resolved isn't running, and lokal's .lokal names need it (systemd 258 or later). lokal sends.lokallookups through systemd-resolved. Ubuntu and Fedora run it by default.this systemd-resolved has no DNS delegates, which lokal's .lokal names need (systemd 258 or later). The machine’s systemd is older than 258, such as Ubuntu 24.04 (255) or Debian 13 (257). Setup can’t run there yet.programs here don't ask systemd-resolved for names (…). resolved runs, but programs look names up elsewhere: the hosts line of/etc/nsswitch.confhas noresolve, and/etc/resolv.confdoesn’t point at resolved’s stub. The message ends with the fix: on Debian and Ubuntu,sudo apt install libnss-resolve. WSL writes its ownresolv.conf, so under WSL you need it.no system certificate store: install the ca-certificates package. Neitherupdate-ca-certificatesnorupdate-ca-trustis installed. Installca-certificates.
After the fix, run lokal setup again. Troubleshooting covers problems after
setup on Linux.
Next#
Setup done? Follow the quick start to create a project and add your first service.