Environment variables
Every setting, what it does, and which ones are runtime editable instead.
Every value is read once at process start and validated with a clear error naming whatever is missing or malformed.
You do not have to set any of this. docker compose up -d works on a fresh
clone with no .env at all, and localdrive setup or localdrive init writes
a complete one for you. This page is for when you want to change something.
A commented file listing every variable with its default ships in the
repository at server/.env.example.
Copy it to .env and edit, or read it alongside this page.
Two that are read before .env is
These are looked up in the process environment only. Putting them in .env
cannot work, because they are what the server uses to decide where .env is.
| Variable | Default | What it does |
|---|---|---|
LOCALDRIVE_HOME |
The platform's own data directory | The install folder. Everything else, including .env and data/, is found relative to this. |
LD_SECRETS_DIR |
Beside the database | Where generated secrets are written. Worth changing only if the database lives somewhere you would rather not keep keys. |
LOCALDRIVE_HOME is the one to reach for when running as a system service,
where the account has no home directory of its own. See
Keeping it running.
A key written twice
Changing a setting by appending a line is the obvious thing to do:
echo 'LD_PORT=80' >> .envIt leaves the file with LD_PORT in it twice, and the first one wins. The
server takes the first value it sees, and so does localdrive status, so the
two agree with each other, but neither uses the line you just added.
Edit the existing line rather than appending a second one. A real environment variable still beats the file either way.
Secrets are generated, not typed
JWT_SECRET, MOUNT_HELPER_SHARED_SECRET, and LAN_DISCOVERY_SHARED_SECRET
are created on first start if they are not already set, and written to
data/db/secrets.env with owner only permissions. Each mixes fresh randomness
with an identifier unique to that machine, so two installs never share one.
They are written down rather than regenerated each start, because a signing key that changed on every restart would invalidate every token in existence.
Setting any of them in the environment overrides the generated one. Deleting the file regenerates them, which signs everyone out.
How it is reached
| Variable | Default | Notes |
|---|---|---|
LD_PORT |
7443 |
The one port everything serves on |
LD_DOMAIN |
empty | Leave empty unless you own one. Setting it turns on HTTPS |
LD_TLS_EMAIL |
empty | Renewal notices, only with a domain |
LD_TLS |
auto |
off stops the server asking for its own certificate. Docker pins this, since Caddy holds it there |
PUBLIC_BASE_URL |
empty | Absolute base for share and invite links |
CORS_ALLOWED_ORIGINS |
empty | Empty answers any origin. Set it to a comma separated list to narrow that. See below |
Which pages may talk to your server
CORS_ALLOWED_ORIGINS is empty by default, and empty means any origin may
call the API. That is deliberate, and it is worth understanding before you
decide whether to change it.
Everyone runs their own server. The page talking to it is one somebody else hosts, on a domain this server has never heard of, so a list of permitted origins is not something it can know in advance. Refusing by default would mean every browser client fails until its address is typed into a config file.
What that does and does not mean:
- It does not make your files public. Every request still needs a token, and the browser still refuses to send credentials it does not have. A page that cannot sign in cannot read anything.
- It does mean a page on any domain can attempt a call. It gets an unauthorised answer without a token.
- Media is readable cross-origin too. Thumbnails and video are fetched by the element itself, and a stricter policy makes the browser drop the response, which shows up as every picture being broken.
Set it when you know exactly which client you use and want the smaller surface:
CORS_ALLOWED_ORIGINS=https://drive.example.com,https://files.example.comExact origins, comma separated, scheme included. Once it is set, anything not listed is refused, including the web client if you forget to add it.
The websocket follows the same rule, so a client that may call the API may also open a socket to it.
Process
| Variable | Default |
|---|---|
APP_ENV |
production |
LOG_LEVEL |
info |
LISTEN_ADDR |
:8080 |
Storage
| Variable | Default |
|---|---|
DB_PATH |
/data/db/localdrive.sqlite |
LIBRARY_PATH |
/data/library |
EXTERNAL_MOUNTS_PATH |
/data/external |
DATA_DIR |
./data (host side) |
Helpers
| Variable | Notes |
|---|---|
MOUNT_HELPER_SOCKET |
Empty disables drive management |
MOUNT_HELPER_SHARED_SECRET |
At least 16 characters when the socket is set |
LAN_DISCOVERY_SOCKET |
Empty disables network announcing |
LAN_DISCOVERY_SHARED_SECRET |
At least 16 characters when the socket is set |
Authentication
| Variable | Default | Notes |
|---|---|---|
JWT_SECRET |
none | Required, at least 32 characters, rejected if it looks like a placeholder |
ACCESS_TOKEN_TTL |
15m |
|
REFRESH_TOKEN_TTL |
30d |
Accepts a d suffix |
ARGON2_MEMORY_KIB |
65536 |
64 MB per hash in flight; raise if you have RAM |
ARGON2_TIME |
2 |
|
ARGON2_THREADS |
2 |
Limits
| Variable | Default |
|---|---|
MAX_UPLOAD_CONCURRENCY |
4 |
WORKER_POOL_SIZE |
2 |
MAX_READ_CONNS |
4 |
DEFAULT_QUOTA_BYTES |
0, meaning unlimited |
TRASH_RETENTION_DAYS |
30 |
VERSION_RETENTION_COUNT |
20 |
VERSION_RETENTION_DAYS |
180 |
Seeds, not settings
These three only apply the first time the server starts. After that they live in the database and are changed in Settings, Server, because deciding later to open or close signups should be a normal setting change rather than a redeploy.
| Variable | Default |
|---|---|
ENABLE_LAN_DISCOVERY_DEFAULT |
true |
REQUIRE_DEVICE_APPROVAL_DEFAULT |
true |
ALLOW_SELF_REGISTRATION_DEFAULT |
false |
Optional
| Variable | Notes |
|---|---|
ENCRYPTION_KEY |
Turns on per file encryption at rest; incompatible with the browsable mirror |
METRICS_ENABLED |
Exposes /metrics in Prometheus text format |
LOCALDRIVE_FETCH_FFMPEG |
Set to false to stop the server fetching ffmpeg by itself. See Requirements |