Docs · DevCatz

Sites, domains and HTTPS

Link, clone or park projects as sites, pick their domains, trust the local certificate on macOS or Windows, and run monorepos and worktrees.

A site is a project folder DevCatz serves under a name of its own, such as https://shop.localhost. Every site gets a page in Projects with a header, a row of tabs and its own settings. This page covers adding sites, their domains and certificates, how Start and Stop behave, what happens when a folder goes missing, and the bigger shapes: monorepos, folders of apps and Git worktrees.

Adding a site

Press Create New Site at the top of the Projects sidebar (or Create New Site… in the menu bar / tray menu). The dialog offers three ways in.

Link a project folder… — Choose the folder, then set its name, domain and Node.js version. DevCatz looks at the folder, detects the stack (see Detected stacks) and fills in the site form.

Clone from Git

Clone from Git… — A repository of your GitHub / GitLab / Gitea / Bitbucket account, or any clone URL.

Field What it does
Repository URL Any clone URL, or pick one from Your repositories… (the accounts connected under Git)
Into folder The parent folder the repository is cloned into
Name The folder name, and the site name
Shallow (latest commit only) Faster for big repositories; fetch the rest later
Branch or tag Clone that one instead of the default branch

The clone shows live progress. Cancel clone stops it and leaves nothing behind. When it is done, the usual site form follows.

Park a folder of projects

Park a folder of projects… — Every project inside becomes a site, now and when you add more later. Drop a new project into a parked folder and it shows up as a site by itself.

Turn on Park as a group and give it a Group name to keep those sites together: the folder’s sites, and the ones added later, sit in a sidebar group named after the folder. Renaming or deleting the group in the sidebar carries over to the parked folder.

Parked folders are listed under Settings → Project → Parked folders, where Unpark removes one. A single site of a parked folder can be unlinked like any other (Unlink… in its Settings tab): it then stays out of the parked folder, listed there as unlinked with a Bring back button, until you bring it back.

Import from Laravel Herd

On a Mac with Laravel Herd sites that DevCatz does not serve yet, the dialog shows a quiet link, Import N sites from Laravel Herd…. DevCatz reads Herd’s linked sites and parked paths, lets you tick the ones you want, and serves them on its own domain suffix. Herd itself is not changed. This import reads Herd’s Mac folders, so it is Mac-only.

The site form

Field What it does
Project name The site’s name in DevCatz
Domain Filled in as <name>.localhost; change it if you like
Protocol HTTPS or HTTP
PHP version / Go version / Node.js version Shown for the stacks that need them
Start the dev server now Starts the site as soon as it is created

Under More settings:

Field What it does
Package manager bun, npm, pnpm or yarn (Node.js projects)
Dev command Leave empty to use the detected one
Let DevCatz assign dev ports Each app gets its own port from 4100 up, so two sites never fight over 3000
Start automatically when DevCatz launches This site starts with DevCatz

Press Create site.

Domains and your own TLD

Every site starts at <name>.localhost. Browsers send .localhost names to your own computer without any DNS. On Windows that is true only for browsers — see DNS rules on Windows for the rest.

A different suffix

To use another suffix such as .test or .devcatz, go to Settings → General → Default domain → Suffix → Change…. DevCatz shows which sites would be renamed; Rename N site(s) applies it.

Some suffixes are refused:

  • Real public TLDs such as .com or .dev, so you never shadow a real website.
  • .local, which belongs to mDNS (Bonjour).
  • A suffix another tool already resolves, such as Laravel Herd’s /etc/resolver/test with a DNS rule (NRPT) of its own. Free that suffix first, or pick another.

When you change the suffix, every site still on the old one is renamed. Domains you set by hand stay as they are.

The site page then lists .env values that still point at the old domain (APP_URL, CORS_ORIGIN, NUXT_PUBLIC_* and the like). The proxy overrides those per request, so the site keeps working; your .env file is not changed.

A suffix other than .localhost needs DevCatz’s own small DNS server, wired in through a file in /etc/resolver. Press Install resolver for .<tld>… (Settings → General, or the third Welcome card once the suffix is set). The steps, shown while it works:

  1. Start DevCatz’s DNS on 127.0.0.1:53531
  2. Create /etc/resolver/<tld> — your administrator password
  3. Check that *.<tld> resolve to this Mac

Remove resolver takes it out again (Remove /etc/resolver/<tld> — your administrator password, then Check that they are gone). The badge next to the suffix reads *.<tld> resolves or does not resolve yet.

DNS rules on Windows

On Windows only browsers (Chrome, Edge, Firefox) resolve .localhost by themselves. Node, PHP, Python and curl do not, so a site that calls another by name — an API from a frontend, a webhook, a test suite — fails. That is why DevCatz on Windows always offers a DNS rule, even for .localhost: Settings → General → Default domain → Add DNS rule for .<tld>…, or the third Welcome card, Site names for your code. The steps, shown while it works:

  1. Start DevCatz’s DNS on 127.53.0.1:53
  2. Add Windows DNS rules (NRPT) for *.<tld> and *.localhost — administrator approval
  3. Check that *.<tld> and *.localhost resolve to this PC

DevCatz’s DNS listens on 127.53.0.1, a loopback address of its own, because a Windows DNS rule names a server and never a port, and it answers the suffix and localhost with 127.0.0.1 and ::1. The rules are kept by Windows and survive restarts; Remove DNS rules takes them out (Remove the DevCatz DNS rules (NRPT) — administrator approval, then Check that they are gone), and so does the uninstaller. The badge next to the suffix reads *.<tld> resolves or does not resolve yet.

DoCatz runs the same DNS on the same address. When it is already there and answers DevCatz’s names, DevCatz simply uses it, and Settings → General → Default domain says so: DoCatz answers these names on the same address (it runs the same DNS), so DevCatz uses it. Once DoCatz quits, DevCatz takes the address back by itself.

If something else has port 53, DevCatz names the program on that address and offers Try again: a Mobile hotspot or Internet Connection Sharing (ICS) in Services, Docker Desktop (quit it), or another program to close.

No two things on one name

DevCatz checks every domain against other sites, apps inside sites, worktrees, Reverb, every DevCatz service and the database domain. While a name clashes, Save (and mod+S) stays disabled.

HTTPS and the local certificate

DevCatz makes its own certificate authority, DevCatz Local CA, valid for 10 years. Certificates for each host are made on demand when a browser asks for that name, and are valid for 397 days.

Your browser trusts these once the CA is trusted. Do it in the Welcome dialog, or under Settings → Project → HTTPS certificates with Trust certificate authority…. The steps are shown while it works:

  1. Prepare the DevCatz Local CA
  2. Add it to your login keychain — macOS asks for your password or Touch ID
  3. Check that it is trusted (Node.js and Bun get it too)

Once it is trusted the button reads Remove from keychain (Remove it from your login keychain — macOS asks for your password, then Check that it is gone). Show ca.crt reveals the file in Finder.

  1. Prepare the DevCatz Local CA
  2. Add it to your Trusted Root certificates — Windows asks install this certificate?
  3. Check that it is trusted (Node.js and Bun get it too)

The CA goes into your own user’s Trusted Root store, so no administrator is needed. If Windows’ question is not in front, click the flashing key icon in the taskbar. Once it is trusted the button reads Remove from Windows (Remove it from your Trusted Root certificates — Windows asks “delete this certificate?”, then Check that it is gone). Show ca.crt shows the file in File Explorer.

When it is done, reload your browser tabs. A few programs keep their own list of trusted certificates:

  • Firefox needs security.enterprise_roots.enabled set to true in about:config, or ca.crt imported under its own certificate settings — on both platforms.
  • Node.js apps that call your HTTPS domains need NODE_EXTRA_CA_CERTS pointing at ca.crt.

What DevCatz changes on your computer explains what trusting does and how to undo it. If a browser still complains, see the troubleshooting page.

The proxy ports

All sites are served by one proxy. By default it listens on:

Port For
8443 HTTPS
8180 HTTP

Change them under Settings → Project → Proxy and press Apply. The badge next to Proxy reads listening or down.

URLs without a port

With the defaults a site lives at https://shop.localhost:8443. To drop the :8443, press Enable clean URLs… (Settings → Project → URLs without a port, or card 2 of the Welcome dialog, Names without a port). The steps are shown while it works:

  1. Check ports 443 and 80
  2. Install the two port forwarders — your administrator password
  3. Check that sites answer without :8443

The forwarders are launchd services (LaunchDaemons) on [::1]:443 and [::1]:80, which relay to the proxy on 8443 / 8180. Disable removes them (Remove the two port forwarders — your administrator password, then Check that sites answer on :8443 again).

  1. Check ports 443 and 80
  2. Forward ports 443 and 80 to DevCatz — administrator approval
  3. Check that sites answer without :8443

The forwarding is Windows’ own port proxy (netsh interface portproxy, run by the IP Helper service) from [::1]:443 and [::1]:80 to the proxy on 8443 / 8180; it takes one administrator prompt, and the rules survive restarts. Disable removes them (Remove the forwarding of ports 443 and 80 — administrator approval, then Check that sites answer on :8443 again).

127.0.0.1 is covered too when nothing else listens on 127.0.0.1:443 and :80. If another program already holds them, clean URLs work over IPv6 only until it lets go; the card then says when IPv4 has become free, so you can disable and enable again. See What DevCatz changes on your computer and URLs without a port stopped working.

The site header

At the top of a site’s page:

  • Status: a dot and a badge, ★ for a favorite, the group badge, a parked badge, a worktree · <branch> chip for worktrees, and the URL chip.
  • Always on — for PHP sites with nothing extra to run (see Start and stop).
  • Start, or Start ▾ with Start and Start all (the site plus its worktrees). While it starts the button reads Starting n/m… or Building <package>…, with ✕ to cancel.
  • Stop and Restart.
  • Build, or Build ▾ with Build all and each app: npm / pnpm / bun run build, the Vite or Mix assets of PHP sites, or go build -o bin/<name>.
  • Open ▾ — each app’s domain, your Editor, FinderFile Explorer and Terminal (Warp, iTerm or Ghostty when installed, else TerminalWindows Terminal when installed, else a Command Prompt).
  • Share — share the site on a public URL (see Expose). While it is shared, the button reads Shared and a chip shows the public URL (public · click to copy).

Below the header sit the site’s tabs. Those that do not fit move into More N. A site’s tabs walks through each one.

Desktop apps

An Electron or Tauri project is a site without a web address. Its header shows Desktop app · opens in its own window with the framework’s mark instead of a URL chip, there is no Share, Open ▾ has no browser entries, and the sidebar subtitle reads desktop app. In place of the preview, the Overview shows a card, A desktop app: Start opens its window, the way npm start does in a terminal. It has no web address, so there is nothing to preview or share here; its output is in Logs. In the Apps table its address reads — (its own window), and the site’s Settings tab has no Domain or Protocol. A Tauri frontend’s dev port stays listed.

Per-site settings

The site’s Settings tab:

Group Settings
Site Name, Domain (or Subdomain for a worktree), Protocol — the last two not for a desktop app
Dev server Dev command (empty = detected), Package manager (bun / npm / pnpm / yarn), Node.js version, PHP version, Python version, Go version
Ports and start-up Let DevCatz assign dev ports (off = ports come from .env), Start automatically when DevCatz launches
Apps: address and port Per app: a subdomain <sub>.<site> (empty = the site’s own domain, Default = back to the default), Generate for a free port, Also write API_PORT to .env (only while the Env tab is unlocked)
Runs with Start Queue worker (php artisan queue:work), Scheduler (php artisan schedule:work), Celery worker, Celery beat
Free up space Move node_modules, vendor/, Python virtual environments or Rust target/ to the TrashRecycle Bin. Stopped sites only; Install dependencies brings them back

Buttons at the bottom:

  • Save (mod+S).
  • Re-detect stack — look at the folder again after you change its framework or layout.
  • Unlink… — type the domain to confirm. DevCatz forgets the site; the project files are untouched. A site from a parked folder stays out of it until you press Bring back under Settings → Project → Parked folders.

Some apps hard-code their port in the source. DevCatz cannot give those a managed port, and Apps: address and port explains why for that app.

Start and stop

What Start does depends on the stack:

  • PHP sites are always on. PHP-FPMPHP (FastCGI) serves them as soon as they exist. Start only runs the extras: Vite (with laravel-vite-plugin on the same origin, honouring public/hot), Laravel Mix watch, the queue worker, the scheduler and Reverb. A PHP site with none of these shows Always on instead of Start.
  • Sites with several apps start app by app. Stop waits until every app has started before it stops them.
  • One app can be restarted on its own with ↻ under Overview → Apps.
  • Sites without a port — Burn, Candle, command-line tools — count as running once started.
  • Desktop apps open their own window (see Desktop apps).

A few safety nets:

  • Start all sites on launch (Settings → Project → Sites) is off by default: only sites with Start automatically when DevCatz launches in their own settings start. The site you had selected opens again.
  • When you quit DevCatz or it updates itself, it remembers which sites were running and starts them again on the next launch.
  • If the runtime is missing, Start says so instead of failing quietly. When dependencies are missing, Install dependencies appears.
  • Install dependencies always answers. With nothing to install from it says so — No package.json here to install from: use Re-detect stack (Settings) if the project changed, or No package.json, composer.json, go.mod, Cargo.toml or pyproject.toml in the site’s folders to install from — and when Bun is the package manager but is not installed, it points at the Bun page. Afterwards a notice reads Dependencies installed, or The install finished, but … See Logs. when a folder still lacks its packages.
  • Before you press Start, the Overview says why it would fail: Some packages are not installed names the packages package.json lists that node_modules does not have yet (per app folder in a monorepo), and Electron is not fully installed — its binary download never ran — comes with Repair Electron.
  • A broken virtualenv is caught too. When a Python project’s .venv was made with a Python that is no longer installed, the Overview shows The virtualenv is broken and Start refuses; Install dependencies moves the old .venv to the TrashRecycle Bin, makes it again with the site’s Python and installs the packages.

When a site’s folder is gone

A linked site whose folder was deleted, renamed or moved outside DevCatz stays in the sidebar, dimmed, with the line This site is gone. Its page shows the path where the folder was and two buttons (also in the right-click menu):

  • Locate… points the site at the folder where it is now, keeping its domain, ports and settings.
  • Remove forgets the site.

Putting the folder back brings the site back by itself. If the folder you pick in Locate… looks like a different project (another folder name, another manifest name), DevCatz asks — This folder looks like another project. It is X, not Y. — and offers Link as a new site (it gets its own domain and default settings; the missing site stays in the list as gone), Use it for Y (it takes over Y’s domain and keeps its settings: dev command, runtime versions and so on), or Cancel.

Monorepos and folders of apps

JavaScript workspaces

A project with npm, yarn or bun workspaces, or a pnpm-workspace.yaml, is one site with several apps. Each app gets a managed port. Plain vite, astro, nuxt or next dev scripts get --port N added (and --strictPort for Vite), so the app really uses the port DevCatz gave it.

  • When the root has no dev script (say apps/web and apps/api), Start runs each app from its own folder.
  • Go modules and Rust crates inside the workspace become apps too. DevCatz looks in apps/, services/, backend/, server/, packages/ and the root.
  • PHP and Python projects inside the workspace run as apps of their own — say apps/api with artisan or a pyproject.toml beside apps/web, with or without a package.json of its own. Install dependencies runs Composer or uv in them too, since the JavaScript package manager skips them.
  • A dev script that sets its own --port (TanStack Start’s starter does) gets DevCatz’s managed port instead.

A PHP or Python site with frontend folders

A Laravel, Django or FastAPI project at the root, with other projects in its folders — Laravel with a Vite app in frontend/, Django with frontend/, FastAPI with a NestJS api/ — stays one site. The root keeps the site’s domain (served by PHP-FPMPHP (FastCGI), or by its own dev server), and each folder runs as an app on its own subdomain and port. A PHP site’s folder apps start with it, and the site counts as running once they answer.

A folder of separate projects

Some repositories have a root that runs nothing itself, with project folders one level down (or in apps/, services/, packages/ or projects/). DevCatz runs each folder with its own driver:

Folder holds Runs with
A JavaScript app Its package manager’s dev script
Laravel php artisan serve
Other PHP php -S
Python The detected framework’s command
Go The Go dev command

This applies when you link such a folder. A parked folder keeps its sub-projects as separate sites instead.

Domains for apps

The main web app — the folder named web, frontend, front, app, site, client, www or ui — gets the site’s bare domain. The others get <folder>.<domain>, for example api.shop.localhost.

.env URLs that point at an app’s old port are rewritten to its managed port, and the Logs tab has a chip per app (turbo’s web:dev: prefixes are recognised too).

Worktrees as sites

A Git worktree can become a site of its own, so two branches run side by side. A worktree site is a child of its parent: the sidebar indents it under the parent, and it gets its own ports and a domain built from the branch:

Site Worktree domain
One app <branch>.<site>.<tld>, e.g. feat-login.shop.localhost
Several apps <app>.<branch>.<site>.<tld>

Making a worktree

Open the site’s Git tab → Worktrees → New worktree…:

  • New branch or Existing branch, Start from, and Folder — by default outside the repository, at <repo>.worktrees/<branch>.
  • Dependencies: Install fresh, Leave out, or clone them from the parent. On a Mac the clone uses APFS copy-on-write, so it is quick and takes almost no extra disk space.
  • Database: shared with the parent by default, or a separate copy such as shop_feat_login. SQLite files are copied.

The .env file is copied with the domains rewritten. The queue worker, scheduler and Reverb stay off in a worktree. Each worktree gets its own Redis database index (Pick a free one) and its own Docker compose project name.

Worktrees made elsewhere

  • Add as site turns a worktree made outside DevCatz into a site. Forget missing clears ones that no longer exist.
  • Add new worktrees as sites automatically catches worktrees made by Claude Code, other agents or git worktree add, with a notification and the preview URL. Worktrees you removed by hand are remembered and not added again.

Removing a worktree

Remove worktree… warns about uncommitted or unpushed work and offers Also drop its database when it had its own copy. More on worktrees and branches is on Git.

When a runtime is missing

The site’s Overview tells you what the project needs, for example PHP is needed or the project asks for Node 20, with an Install button for that runtime and version. DevCatz picks the newest version that fits what the project asks for in composer.json, .nvmrc, engines or go.mod.

Sites that call a framework tool — pnpm, Yarn, Air, Poetry and the like — find it on the runtime’s Tools tab, where it carries a needed by badge.

Rust sites list each missing tool: Rust itself, Trunk, Dioxus CLI, cargo-leptos, Tauri CLI, Solana CLI, Anchor, the WebAssembly target, or the SQLx, SeaORM or Diesel CLI. See Runtimes for each one. On Windows, Solana and Anchor run inside WSL, as their docs say; install them there.

Last updated 2026-11-03