Service presets
Service presets are the YAML-driven definitions for every service lerd manages. There are two kinds:
- Default presets (
default: true), the always-recognised services that ship with lerd:mysql,redis,postgres,meilisearch,rustfs,mailpit. They get auto-listed inlerd serviceeverywhere; their lifecycle is identical to add-on presets but they don't need an explicit install step. - Add-on presets: opt-in installers for phpMyAdmin, pgAdmin, MongoDB, alternate MySQL / MariaDB versions, Selenium, Stripe Mock, Memcached, Valkey, RabbitMQ, Soketi, Beanstalkd, Elasticsearch, OpenSearch, Typesense, Typesense Dashboard, Elasticvue, RedisInsight.
Both kinds use the same YAML schema in internal/config/presets/*.yaml and the same code path. Adding or replacing a default service is a YAML edit, not a code change. See Service updates for the configuration knobs (update_strategy, track_latest, allow_major_upgrade).
The external service store
Beyond the presets bundled in the binary, lerd can fetch presets from an external store repo, so new services can be published without shipping a new lerd release. This mirrors the framework store: the presets live in the lerd-env/services repo as a flat index.json plus one <name>.yaml per preset.
lerd service search # list everything the store offers
lerd service search search-engine # filter by name, description, or family
lerd service preset <name> # install a store preset (fetched on demand)lerd service search shows whether each hit is already installed, available locally (bundled or cached), or only in the store. Installing a store-only preset fetches its YAML, validates it, and caches it under ~/.local/share/lerd/service-presets/, after which it behaves exactly like a bundled preset. A cached preset older than 24 hours is refreshed opportunistically on the next install. A store preset of the same name as a bundled one supersedes the built-in only when it validates.
The binary embeds the default presets as a permanent offline fallback, and lerd install and lerd update re-fetch the store preset backing every installed service into that same cache, so an add-on service (pgAdmin, phpMyAdmin, and the rest that live only in the store) keeps resolving by name, config-mount files included, even when the store is later unreachable. If the store can't be reached during install the previously cached copy is left in place, so an offline install never breaks a service that already resolved.
Point LERD_SERVICES_BASE_URL at an alternate base (comma-separated for several) to use a private or local store instead of lerd-env/services.
Config-file mounts
A preset can mount config files into its container via a files: block, so a store preset can ship the same admin-dashboard auto-login and tuning files the bundled presets use:
files:
- target: /etc/phpmyadmin/config.user.inc.php
content: |
<?php
// static file; may read env injected via dynamic_env at runtime
- target: /pgpass
mode: "0600"
chown: true
generator: pgadmin_pgpass # dynamic content from a built-in generatorStatic files are declared inline with content. A file whose body must be computed at install time (for example pgAdmin's servers.json, built from the discovered Postgres family) names a built-in generator instead. Presets can reference the existing generators but can't ship new ones, that needs a lerd release, so most auto-login patterns (a static file reading dynamic_env values, as phpMyAdmin does) need no generator at all. Files are re-sourced from the preset on every service start, so updating lerd or the store definition rolls out new contents without a reinstall. Only presets may declare files; a files: block in a hand-written custom service is stripped on load.
Default service presets
| Preset | Default image | Update strategy | Notes |
|---|---|---|---|
mysql | docker.io/library/mysql:8.4 (LTS) | minor (track_latest) | Multi-version: 8.4 canonical + 9.7 LTS, 5.7 alternates. Every version defaults to the family port 3306; the guard shifts a later sibling off it when one is already installed. SQL migration supported. |
postgres | docker.io/postgis/postgis:16-3.5-alpine | minor (track_latest) | Multi-version: 16 canonical + 17, 18 alternates. Every version defaults to the family port 5432; the guard shifts a later sibling off it when one is already installed. SQL migration supported. |
redis | docker.io/library/redis:7-alpine | minor (track_latest) | Forward-compat across 7.x patches. |
meilisearch | docker.io/getmeili/meilisearch:v1.42 | patch (track_latest) | Cross-minor upgrades require manual dump/restore, automated migration is not offered for Meilisearch (binary dump format is version-specific). |
rustfs | docker.io/rustfs/rustfs:latest | rolling | S3-compatible. |
mailpit | docker.io/axllent/mailpit:latest | rolling | SMTP catcher. |
Add-on service presets
| Preset | Image / versions | Depends on | Dashboard / host port |
|---|---|---|---|
phpmyadmin | docker.io/library/phpmyadmin:latest | mysql (default) | http://localhost:8080 |
pgadmin | docker.io/dpage/pgadmin4:latest | postgres (default) | http://localhost:8081 |
mysql alternates | 5.7 / 9.7 LTS (canonical 8.4 lives in the default preset) | - | family port 3306 (guard shifts later siblings) |
postgres alternates | 17 / 18 (canonical 16 lives in the default preset) | - | family port 5432 (guard shifts later siblings) |
postgres-pgvector | pgvector/pgvector:pg18 (canonical) / pg17 / pg16, pgvector instead of PostGIS | - | family port 5432 (guard shifts later siblings) |
postgres-timescaledb | timescale/timescaledb:latest-pg17 (canonical) / pg16, TimescaleDB time-series extension, enabled automatically by lerd env / lerd link via site_init | - | family port 5432 (guard shifts later siblings) |
mariadb | 12 / 12.3 LTS / 11.8 LTS (default) / 11.4 LTS / 10.11 LTS / 11 (legacy) | - | family port 3306 (guard shifts later siblings) |
mongo | docker.io/library/mongo:7 | - | 127.0.0.1:27017 |
mongo-express | docker.io/library/mongo-express:latest | mongo (preset) | http://localhost:8082 |
selenium | docker.io/selenium/standalone-chromium:latest | - | http://localhost:7900 (noVNC) |
stripe-mock | docker.io/stripemock/stripe-mock:latest | - | 127.0.0.1:12111 |
memcached | docker.io/library/memcached:1.6-alpine | - | 127.0.0.1:11211 |
valkey | docker.io/valkey/valkey:9-alpine | - | 127.0.0.1:6380 |
rabbitmq | docker.io/library/rabbitmq:3-management-alpine | - | http://localhost:15672 (mgmt UI, opens in new tab) |
soketi | quay.io/soketi/soketi:1.6-16-alpine | - | 127.0.0.1:6001 |
beanstalkd | docker.io/schickling/beanstalkd:latest | - | 127.0.0.1:11300 |
elasticsearch | docker.elastic.co/elasticsearch/elasticsearch:8.13.4 | - | 127.0.0.1:9200 |
opensearch | docker.io/opensearchproject/opensearch:2.19.5 | - | 127.0.0.1:9201 |
typesense | docker.io/typesense/typesense:30.2 | - | 127.0.0.1:8108 |
typesense-dashboard | docker.io/bfritscher/typesense-dashboard:latest | typesense (preset) | http://localhost:8084 |
elasticvue | docker.io/cars10/elasticvue:latest | elasticsearch (preset) | http://localhost:8083 |
redisinsight | docker.io/redis/redisinsight:latest | redis (preset) | http://localhost:8085 (opens in new tab) |
# List the bundled presets and their install state
lerd service preset
# Install a single-version preset
lerd service preset phpmyadmin
# Install a specific version of a multi-version preset
lerd service preset mysql --version 5.7
lerd service preset mariadb --version 10.11
# Start it (dependencies are auto-started recursively)
lerd service start phpmyadmin
# Remove it later if you no longer need it
lerd service remove phpmyadminThe web UI exposes the same flow: open the Services tab, click the + button next to the panel header, and pick a preset from the modal. Multi-version presets like mysql and mariadb show a version dropdown next to the Add button. Already-installed presets are filtered out; for multi-version families, only the still-uninstalled versions appear.
The Add button streams per-phase progress while it works, so the spinner label tracks the real step: Writing config…, Starting elasticsearch… for each dependency, Pulling image… with live podman output underneath, Starting service…, then Waiting for ready…. Most of the perceived latency on a first install is the image pull; the progress line shows what layer is being copied so a slow registry is distinguishable from a stuck install.
The detail panel of every database service (built-in mysql / postgres, any installed mongo, and any installed alternate like mysql-5-7), search engine (elasticsearch → Elasticvue, typesense → Typesense Dashboard) and redis → RedisInsight surfaces a sky-blue suggestion banner offering to install the paired admin UI when it isn't installed yet. The banner is dismissable per-preset and dismissal persists in localStorage.
Multi-version presets
mysql, postgres and mariadb ship multiple selectable versions. mysql and postgres mark one version canonical (mysql 8.4 LTS, postgres 16): the canonical version is the default install, recognised as the bare service mysql / postgres on the canonical host port, and the alternates picker only shows non-canonical versions (it doesn't list 8.4 because that IS the default install). mariadb has no canonical version, so there is no bare mariadb service: every version, including the default 11.8 LTS, materialises as a distinct custom service named <family>-<sanitized-tag> (the default resolves to mariadb-11-8). Non-canonical alternates run side-by-side with each other and with the canonical.
Host port shows the family default; the first service to claim it keeps it and a later same-family sibling is shifted once to the next free port (see below).
| Picked | Service name | Container | Host port | Data dir |
|---|---|---|---|---|
mysql 8.4 (canonical) | mysql | lerd-mysql | 127.0.0.1:3306 | ~/.local/share/lerd/data/mysql/ |
mysql 9.7 LTS | mysql-9-7 | lerd-mysql-9-7 | 127.0.0.1:3306 | ~/.local/share/lerd/data/mysql-9-7/ |
mysql 5.7 | mysql-5-7 | lerd-mysql-5-7 | 127.0.0.1:3306 | ~/.local/share/lerd/data/mysql-5-7/ |
postgres 16 (canonical) | postgres | lerd-postgres | 127.0.0.1:5432 | ~/.local/share/lerd/data/postgres/ |
postgres 17 | postgres-17 | lerd-postgres-17 | 127.0.0.1:5432 | ~/.local/share/lerd/data/postgres-17/ |
postgres 18 | postgres-18 | lerd-postgres-18 | 127.0.0.1:5432 | ~/.local/share/lerd/data/postgres-18/ |
mariadb 12 | mariadb-12 | lerd-mariadb-12 | 127.0.0.1:3306 | ~/.local/share/lerd/data/mariadb-12/ |
mariadb 12.3 LTS | mariadb-12-3 | lerd-mariadb-12-3 | 127.0.0.1:3306 | ~/.local/share/lerd/data/mariadb-12-3/ |
mariadb 11.8 LTS (default) | mariadb-11-8 | lerd-mariadb-11-8 | 127.0.0.1:3306 | ~/.local/share/lerd/data/mariadb-11-8/ |
mariadb 11.4 LTS | mariadb-11-4 | lerd-mariadb-11-4 | 127.0.0.1:3306 | ~/.local/share/lerd/data/mariadb-11-4/ |
mariadb 11 (legacy) | mariadb-11 | lerd-mariadb-11 | 127.0.0.1:3306 | ~/.local/share/lerd/data/mariadb-11/ |
mariadb 10.11 LTS | mariadb-10-11 | lerd-mariadb-10-11 | 127.0.0.1:3306 | ~/.local/share/lerd/data/mariadb-10-11/ |
Alternates inherit the preset's update_strategy but with one caveat: they're internally promoted to patch strategy so an alternate explicitly installed at v8.0 doesn't get auto-suggested an 8.4.x upgrade (which would cross the LTS line). Cross-line moves stay in the user's hands via the alternates picker or lerd service migrate.
Each version has its own data directory so they can run side by side. Every member of a family defaults to the family's canonical host port (3306 for mysql/mariadb, 5432 for postgres). The first service to claim the port keeps it; when you install a second same-family service, the port-ownership guard shifts it once to the next free port and records that choice, so it stays put afterwards. A single database of any family therefore lands on the familiar canonical port. lerd service port <service> <port> overrides the assignment, and lerd service expose <service> <other:3306> adds an extra mapping.
Canonical version pinning
Each default-preset install records which version's tag was canonical at install time in ~/.config/lerd/config.yaml under services.<name>.canonical_version. That pin survives future canonical flips in the bundled YAML: when a later lerd release promotes (say) postgres 18 to canonical, existing installs whose pin says 16 continue to resolve against 16 and keep their bare service name. New installs land on whatever the YAML currently calls canonical and get pinned to that.
lerd service migrate <service> <tag> is the explicit cross-major path. Beyond the dump/restore, it rewrites canonical_version to the new tag so the next reconcile honors the move instead of reverting to the original pin.
The mysql preset bundles a my.cnf (/etc/mysql/conf.d/lerd.cnf) that enables innodb_large_prefix, Barracuda, innodb_default_row_format=DYNAMIC (via loose- so MySQL 5.6 ignores it), and innodb_strict_mode=OFF. Combined this lets stock Laravel migrations run on every supported version without needing Schema::defaultStringLength(191) in AppServiceProvider.
Discovery metadata
A preset declares how it appears in the web UI, so adding one to the store never requires a change to lerd itself:
category: admin # discovery section: databases, cache, messaging, search,
# mail, admin, storage, testing, other
icon: search # key in the UI icon set
admin_for: # the services this preset's UI administers
- opensearchadmin_for is not depends_on. depends_on orders container startup (and is satisfied by the named service or any installed drop-in whose family or env_role matches), while admin_for says which services a UI can administer. phpMyAdmin declares admin_for: [mysql, mariadb] and depends on mysql; RedisInsight declares admin_for: [redis, valkey] and depends on redis.
lerd matches admin_for against the preset a service was installed from, so a versioned member like mariadb-11-8 still resolves to mariadb. A service the list names gets its admin UI suggested on its service page, and its dashboard button opens that UI once it is installed.
An unrecognised category falls back to other and an unrecognised icon to a generic glyph, so a preset written for a newer lerd degrades rather than breaks.
Service families and admin UI auto-discovery
A preset can declare a family: so admin UIs can find every member with one directive. The bundled mysql and mariadb presets declare family: mysql and family: mariadb respectively. The built-in mysql and postgres services are members of the mysql and postgres families implicitly.
phpMyAdmin uses this with the dynamic_env directive:
dynamic_env:
PMA_HOSTS: discover_family:mysql,mariadbPMA_HOSTS is recomputed at every quadlet generation as a comma-joined list of every running mysql / mariadb family member's container hostname (e.g. lerd-mysql,lerd-mysql-5-7). A stopped member is omitted so the login page does not offer an offline server. Auto-login still works with the preset's static PMA_USER / PMA_PASSWORD.
Admin UIs that talk to a single host (RedisInsight, mongo-express) just pin the canonical dependency host in plain environment, and lerd rewrites it to the service that actually satisfied depends_on at quadlet generation, including an env_role drop-in like Valkey for Redis:
environment:
RI_REDIS_HOST: lerd-redis
ME_CONFIG_MONGODB_URL: mongodb://root:lerd@lerd-mongo:27017/?authSource=admin
depends_on:
- redisAny environment value containing lerd-<dep> for a depends_on entry is retargeted to lerd-<satisfier> (a full URL is rewritten in place). Because the YAML carries no special directive, an older lerd binary that predates this still parses the preset and connects to the canonical host.
A UI that reads numbered env var sets rather than one comma-joined list declares a top-level expand_env block instead, which writes one var per running member with the declared key suffixed _1 … _N:
environment:
RI_REDIS_HOST: lerd-redis
RI_REDIS_PORT: "6379"
RI_REDIS_ALIAS: lerd-redis
expand_env:
RI_REDIS_HOST: redis,valkey={host}
RI_REDIS_PORT: redis,valkey=6379
RI_REDIS_ALIAS: redis,valkey={name}With Redis and Valkey both up that renders RI_REDIS_HOST_1=lerd-redis, RI_REDIS_ALIAS_1=redis, RI_REDIS_HOST_2=lerd-valkey, RI_REDIS_ALIAS_2=valkey and a 6379 port for each, so RedisInsight opens with every installed member already wired. {host} expands to the container hostname and {name} to the service name; a template with neither, like the port above, is simply repeated. Members are sorted by hostname, so the numbering is stable as long as the set of running members is.
expand_env is a separate top-level field precisely so the published YAML stays backward compatible: a binary that predates it ignores the whole block on parse and serves the static environment values, which is why a preset keeps the unsuffixed single-connection keys there as the fallback. A binary that resolves expand_env drops those unsuffixed keys and writes the numbered set instead, so the two never coexist in a rendered quadlet. For the same reason, a dynamic_env directive the running binary does not recognise is warned about and skipped rather than treated as fatal, matching how a file mount naming an unknown generator is skipped.
Lerd automatically regenerates phpMyAdmin's quadlet (and any other consumer of a family directive, an expand_env set, or a pinned dependency host) whenever a family member is installed, removed, started, or stopped, and again at the end of a bulk lerd start / install so engines and admin UIs that come up together still get a correct host list. Active consumers are stop-removed-restarted only when the rendered unit changed, so a steady host list does not bounce phpMyAdmin on every start.
.lerd.yaml preset references
When a service installed via a preset is saved into a project's .lerd.yaml by lerd init, lerd stores a preset reference instead of inlining the full service definition:
services:
- mysql:
preset: mysql
version: "5.6"
- redis
- meilisearchThis keeps .lerd.yaml small and lets each machine resolve the embedded preset locally, picking up any preset improvements in newer lerd versions without churn in the project file. When a teammate clones the project and runs lerd link / lerd setup, lerd checks whether the referenced preset is installed locally and calls lerd service preset <name> --version <ver> under the hood if it isn't.
Hand-rolled custom services that don't come from a preset still inline their full definition into .lerd.yaml for portability; see Custom services.
Dependency rules
A preset's depends_on is enforced two ways:
- At install time: installing a preset is rejected until each dependency is satisfied. A
depends_onentry is met by that service, or by any installed service whosefamilyorenv_rolenames it, so phpMyAdmin'smysqldependency is met by MariaDB (env_role: mysql), RedisInsight'sredisdependency by Valkey, and pgAdmin'spostgresdependency bypostgres-pgvector. When several satisfiers exist, lerd prefers the literal name, then a same-family member, then an env_role drop-in. A drop-in only counts when the consumer can bind it: it must declarediscover_familycovering that dependency (or the satisfier's family), or pin the canonicallerd-<dep>host inenvironmentfor lerd to retarget, otherwise a hardcoded host would install green and then fail to connect. When nothing satisfies a dependency the error lists known alternatives from local presets, the consumer'sadmin_for, and the store index (family/env_role), so MariaDB and Valkey show up on a clean box before those presets have been cached. The Web UI's preset picker disables the Add button with the same gating. - At start/stop time:
lerd service start, the Web UI Start/Stop buttons, the TUI service actions, and MCPservicestart/stop/restart all share oneserviceopspath. Start brings a satisfier for eachdepends_onentry up first (literal name when installed, otherwise a family or env_role drop-in), then regeneratesdiscover_familyand pinned-dependency-host consumers. Stop cascade-stops dependents only when nothing else still running satisfies their dependency; otherwise the dependent stays up and those consumers are regenerated. Starting an engine waits until it is ready before that regen, so the consumer is not rewritten with an empty host list. Bulklerd start/ install refresh everydiscover_familyand pinned-dependency-host consumer once engines are up. Auto-stopping unused services (pause / unlink) uses the soft stop path only and does not set the paused flag, so a laterlerd startstill brings redis and mailpit back.
discover_family is separate: it expands admin UI host lists at quadlet generation time to every running member of the named families, so a stopped database does not appear as a selectable server. CLI / UI / API dependency labels prefer a running satisfier when one exists, so phpMyAdmin shows as depending on MariaDB while mysql is installed but stopped.
Default credentials
| Preset | Sign-in |
|---|---|
phpmyadmin | auto-authenticated against lerd-mysql as root / lerd |
pgadmin | admin@pgadmin.org / lerd (server mode disabled, no master password), pre-loaded with the Lerd Postgres connection via a bundled servers.json + pgpass |
mongo | root user root / lerd |
mongo-express | basic auth disabled, open http://localhost:8082 directly |
stripe-mock | no auth (Stripe test mock) |
memcached | no auth (Memcached has no native authentication) |
valkey | no auth (no password set for local dev, same as redis) |
rabbitmq | management UI: root / lerd (also the default AMQP user) |
soketi | Pusher app id / key / secret all lerd, default cluster mt1 |
beanstalkd | no auth (Beanstalkd has no native authentication) |
elasticsearch | no auth (xpack.security.enabled=false for local dev) |
opensearch | no auth (security plugin disabled for local dev) |
typesense | API key lerd, sent as the X-TYPESENSE-API-KEY header |
typesense-dashboard | no sign-in, opens pre-connected to the lerd Typesense node at localhost:8108 |
elasticvue | no auth, opens straight to the pre-configured Lerd Elasticsearch cluster at http://localhost:9200 |
redisinsight | no sign-in, opens pre-wired to the lerd Redis connection at lerd-redis:6379 |
Database service quality-of-life
When a preset's paired admin UI is installed, the database service's detail panel header gains an Open phpMyAdmin / pgAdmin / Mongo Express button. Clicking it auto-starts the admin service (which in turn auto-starts the database via depends_on) and opens the dashboard URL in a new tab.
When the paired admin UI is not installed and the service is active, the header instead shows an Open connection URL anchor, a real <a> element pointing at mysql://, postgresql://, or mongodb:// so your registered DB client (DBeaver, TablePlus, DataGrip, Compass, etc.) handles it natively. Right-click "Copy link" works.
mongo declares its own connection_url: (see YAML schema in the custom services reference) so it gets the same treatment as the built-in databases.
Declaring entities and actions
A preset can declare what the service holds and what can be done to it through an introspect.entities block. The declared kind databases powers the web UI's Databases tab, the lerd db commands and database snapshots; any other kind (buckets, keyspaces, indexes, queues) gets a generic overview tab on the service's detail page. Everything engine-specific lives in the declared commands, so a new engine, or a new capability on an existing one, ships as a store publish with no lerd release:
introspect:
entities:
- kind: databases
format: sql
list: >
$(command -v mysql || command -v mariadb) -uroot -sN -e
"SELECT s.SCHEMA_NAME, COALESCE(SUM(t.DATA_LENGTH + t.INDEX_LENGTH), 0)
FROM information_schema.SCHEMATA s
LEFT JOIN information_schema.TABLES t ON t.TABLE_SCHEMA = s.SCHEMA_NAME
WHERE s.SCHEMA_NAME NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys')
GROUP BY s.SCHEMA_NAME ORDER BY s.SCHEMA_NAME"
columns:
- key: size
label: Size
format: bytes
actions:
create: >
$(command -v mysql || command -v mariadb) -uroot -e
'CREATE DATABASE IF NOT EXISTS `{{name}}`;'
drop:
exec: >
$(command -v mysql || command -v mariadb) -uroot -e
'DROP DATABASE IF EXISTS `{{name}}`;'
destructive: true
export:
exec: >
$(command -v mysqldump || command -v mariadb-dump) -uroot
--single-transaction --quick --no-tablespaces --routines --triggers --events
{{name}}
filename: "{{name}}.sql"
import: >
$(command -v mysql || command -v mariadb) --max-allowed-packet=1G -uroot {{name}}Every command runs via sh -c inside the lerd-<service> container. The list command prints one entity per line: the name, then one tab-separated field per declared column, in order; lerd parses the output and never branches on the engine name. The fixed lerd admin password is passed through the exec environment (MYSQL_PWD / PGPASSWORD), so commands need no inline credentials for MySQL and PostgreSQL; other engines embed theirs the way their client expects.
In an action, every is replaced by the entity name after validation against a strict pattern (letters, digits, underscores and dashes only, 64 characters at most), which is what makes the substitution safe both as a shell word and inside a quoted SQL identifier. A bare string is shorthand for an action with just an exec. destructive: true makes the UI confirm before running the action. An export action writes its dump to stdout (filename names the download); an import action reads one from stdin, always in the plain form: lerd unwraps a gzipped upload before it reaches the command, so an export may compress for transport without the import having to care. Declaring both is also what enables snapshots for the engine: a snapshot stores the declared export gzipped and restores through the declared import, and the export_all / import_all variants (no ) cover the service-wide snapshots the engine migration flow uses. format: sql on the entity tells lerd the dump is SQL text, which turns on the import sanitizer and the per-statement error tally; leave it off for engines with their own archive format and the bytes stream through untouched.
A service whose own image ships no client tooling can name an image: on the entity, and lerd runs every command in an ephemeral container of that image on the lerd network instead of exec-ing the service container, with the entity's env: pairs carrying the client's connection settings. RustFS is the model case: it holds S3 buckets but no S3 client, so its buckets entity runs through minio/mc. A single action can override the image and env for itself, because one tool rarely covers everything: bucket archives need tar, which the mc image does not carry, so the export and import actions run on rclone/rclone while listing stays on mc. An owner_env: on the entity names the site .env key whose value is the entity a site owns (AWS_BUCKET for buckets), which is what links each row to its site in the UI; only sites whose .env references this service count, so a project pointed at real AWS never claims a local bucket of the same name.
The single-command introspect.list_databases form from before entities existed is still honoured as a list-only databases declaration, so presets published for older lerd versions keep listing.
Removing and reinstalling presets
Default presets can be removed: lerd service remove postgres (or any other) stops the unit, deletes the quadlet, and frees the slot. The preset itself stays available in lerd service preset list as not-installed, so a future lerd service preset postgres brings it back. Pass --purge to also rename the data dir aside.
lerd service reinstall <name> stops, removes, and reinstalls at the current version. --reset-data wipes the data and recreates per-site state on the fresh container (databases for mysql/mariadb/postgres, buckets for rustfs). See custom services for the resolution rules.