This guide explains how to change server-level settings for Marinara Engine using environment variables. An environment variable is a setting you write in a plain text file that the server reads. Most users never need this page. The full variable list is near the bottom.
Marinara Engine works out of the box with no configuration. You only need this page for a small number of tasks. Most of them involve running the server for more than one device.
You might edit configuration when you want to:
- Let other devices on your network reach the server (access control).
- Protect a shared server with a password or an IP allowlist.
- Change where your data is stored on disk.
- Turn up logging to help diagnose a problem.
- Give slow image, video, or embedding jobs more time to finish (timeouts).
- Unlock privileged actions like backups or updates from a remote device.
Almost everything else, like your AI provider keys, characters, and chat options, is set inside the app, not here. To add an AI provider, see Connecting to an AI Provider.
Optional first-party agents are also managed inside the app. Open Agents → Download Agents to install or uninstall them. Marinara automatically selects the Pasta-Devs/Marinara-Agents catalog lane matching its Engine major version.
Package lifecycle and storage:
- Updates: Marinara checks already-installed official packages for compatible updates and asks before downloading each new version. Choosing No keeps the current version and leaves the manual Update action available in Download Agents. A fresh install remains empty until you choose packages.
- Platforms: The same behavior applies to desktop, Docker, and Termux-hosted Android installations. iOS and other browser clients use the packages installed on their Marinara host server.
- Persistence: Packages live under
DATA_DIR/capability-packages. Docker volumes, custom data directories, backups, and normal upgrades preserve them. - Offline resilience: Existing packages continue working at their installed version when outbound GitHub HTTPS is unavailable, an update is declined, or an update fails verification.
External Agent files, folders, and custom repositories are locked by default. To allow them, open Settings → Advanced → Danger Zone and enable Allow custom Agent imports. Unlike External Extensions, this user-controlled gate does not require an environment variable. The import controls remain greyed out until it is enabled.
Every import shows the Agent's requested capabilities before it is stored. Permissions must be approved explicitly, bundled functions and tool selections are not imported, generated CSS is sanitized, and result actions are checked against the approved capability set. Turning the gate off stops externally imported Agents from running. Custom Agents created in Marinara and official packages installed through Download Agents remain runnable and do not use this gate.
Custom repositories are disabled by default because their prompts and tool selections are unvetted third-party content. Set ENABLE_CUSTOM_AGENT_REPOS=true, enable Allow custom Agent imports in the Danger Zone, then open Agents → Download Agents → Custom Sources to preview a public GitHub repository. Adding a source and applying any later content change both require explicit confirmation. Synchronization is manual; Marinara does not clone repositories or poll them in the background.
The repository root must contain an agents.json array using the same agent-definition format as downloadable agent packages. A minimal file looks like this:
[
{
"id": "continuity-helper",
"name": "Continuity Helper",
"description": "Checks recent turns for contradictions.",
"phase": "post_processing",
"enabledByDefault": false,
"category": "writer",
"defaultPromptTemplate": "Check {{messages}} for continuity errors."
}
]Marinara accepts GitHub repository-root URLs only and validates the bounded archive plus every agent definition before showing the preview. During synchronization, remote prompt, settings, and tool values replace the repository-managed values shown in that preview. Connection and artwork choices remain local. If an agent disappears upstream, Marinara keeps it as a normal local custom agent and removes only its repository link. Removing a source follows the same keep-local policy.
External Extension imports require two independent opt-ins. Set ENABLE_EXTERNAL_EXTENSIONS=true in .env, then open Settings → Advanced → Danger Zone, scroll below the data-deletion controls, read the warning, and enable Allow third-party extension imports. Only then does the External Extensions section appear under Settings → Addons.
The environment variable is the host-operator permission; the Danger Zone toggle is the user's explicit acceptance. The section, import routes, approval routes, and both runtime loaders all enforce the combined policy. Closing either gate disables external records and stops running external code. Manually stored, legacy, profile-imported, and unknown-source extension records are treated as external, so dropping files into an extension-related folder cannot bypass the gates.
Professor Mari drafts remain available without this flag. They are created disabled and still require approval of their exact code hash.
Sandboxed Browser Extensions remain the default. Some older third-party packages are marked Full page access because they depend on Marinara's DOM. That mode runs the exact approved code inside Marinara's page and can access page content, browser storage, network APIs, and the current same-origin session. It is available only to External Extensions after both gates are open and requires a separate warning acknowledgement. Disable it and reload the page if the extension leaves visual or behavioral changes behind.
Configuration lives in a file named .env. This is a plain text file with one setting per line, in the form KEY=value. Lines that start with # are comments and the server ignores them.
The .env file is data, not a shell script. Marinara does not execute $, command substitutions such as $(...), or other shell syntax found in a value. The macOS/Linux and Termux launchers use the same non-evaluating rule for the small set of settings they need before server startup. A value already supplied in the launcher's environment takes precedence over the matching .env entry.
Marinara creates an empty .env for you the first time it starts, so you do not have to make one by hand.
- On normal installs, the
.envfile sits in the project root folder. - On official Docker or Podman images, it sits at
/app/data/.env, inside the same storage volume as your data.
A file named .env.example in the same folder lists every setting with its default. To change a setting, copy the line from .env.example into .env, then edit the value after the = sign.
Here is a sample .env that changes the port and enables a password:
PORT=8080
BASIC_AUTH_USER=alice
BASIC_AUTH_PASS=correct-horse-battery-staple
The server reads .env by itself, no matter how you start it. This includes running pnpm start directly. The shell launchers (start.bat, start.sh, start-termux.sh) add two extras. They set HOST=0.0.0.0 so other devices can reach the server, and they open the browser for you. With bare pnpm start, the server listens only on this computer unless you set HOST yourself.
Marinara watches the .env file while it runs. When you save a change, most settings take effect within about 2 seconds, with no restart. The server writes a log line starting with [env-watcher] each time it applies a change.
A small group of low-level settings are locked in when the server starts. Changing them needs a full restart. These settings are:
PORT,HOSTSSL_CERT,SSL_KEYDATA_DIR,FILE_STORAGE_DIRENCRYPTION_KEYMARINARA_ENV_FILETZAUTO_OPEN_BROWSER,AUTO_UPDATE_ENABLED,AUTO_CREATE_DEFAULT_CONNECTIONLOG_DISABLE_REQUEST_LOGGING- The image, video, sprite, and ComfyUI timeout and poll settings (
IMAGE_GEN_TIMEOUT_MS,VIDEO_GEN_TIMEOUT_MS,VIDEO_GEN_MAX_RESPONSE_BYTES,SPRITE_GENERATION_TIMEOUT_MS,SPRITE_ANIMATED_FFMPEG_TIMEOUT_MS,COMFYUI_GEN_TIMEOUT, and the four*_VIDEO_POLL_INTERVAL_MSsettings)
When one of these changes, the log warns that a restart is required. Access-control settings and secrets like BASIC_AUTH_USER, BASIC_AUTH_PASS, IP_ALLOWLIST, ADMIN_SECRET, and CSRF_TRUSTED_ORIGINS do not need a restart.
Access control decides who is allowed to reach a running server. This section is a quick reference. For a step-by-step walkthrough with examples, read Remote Access: Basic Auth and IP Allowlist.
A few terms used below:
- Loopback means the same computer the server runs on. You reach it at
127.0.0.1orlocalhost. - A CIDR range is a short way to write a whole block of IP addresses, like
192.168.1.0/24. CIDR stands for Classless Inter-Domain Routing. - RFC 1918 ranges are the standard private address ranges used inside home and office networks, such as
10.x.x.xand192.168.x.x.
By default, when you set no password, the server accepts connections only from trusted sources. Those are loopback, any address in IP_ALLOWLIST, Tailscale, and same-host Docker bridge/gateway traffic. Every other caller, including your normal home network, gets a 403 Forbidden until you pick one of the options below.
The main access-control settings are:
| Variable | Default | What it does |
|---|---|---|
BASIC_AUTH_USER |
empty | Username for a password prompt. Set with BASIC_AUTH_PASS to require a login. |
BASIC_AUTH_PASS |
empty | Password for the login prompt. Leave either field empty to turn login off. |
BASIC_AUTH_REALM |
Marinara Engine |
Text shown in the browser's password box. |
IP_ALLOWLIST |
empty | Comma-separated IPs or CIDR ranges that are always allowed. Loopback is always allowed. |
IP_ALLOWLIST_ENABLED |
true |
Set to false to keep the list but pause enforcement. |
ALLOW_UNAUTHENTICATED_PRIVATE_NETWORK |
false |
Restores passwordless access from private networks when no login is set. |
ALLOW_UNAUTHENTICATED_REMOTE |
false |
Allows passwordless access from any address, including the public internet. Not recommended. |
TRUSTED_PRIVATE_NETWORKS |
built-in defaults | Replaces the default private-network ranges. Include any defaults you still want. |
BYPASS_AUTH_TAILSCALE |
true |
Lets Tailscale traffic skip the login and allowlist. |
BYPASS_AUTH_DOCKER |
true |
Lets Docker bridge traffic and the exact default gateway detected inside Docker skip the login and allowlist. |
REQUIRE_AUTH_FOR_DOCKER_PROXY |
true |
Requires normal login/allowlist checks for proxy-forwarded Docker traffic. Set to false only when every upstream client is trusted. |
TRUSTED_HOSTS |
empty | Extra public or reverse-proxy hostnames Marinara may answer. Direct IP, localhost, .local, .home.arpa, and single-label LAN names work automatically. |
SSL_CERT |
empty | Path to a TLS certificate file. Set with SSL_KEY to serve HTTPS directly. |
SSL_KEY |
empty | Path to the TLS private key file. |
CSRF_TRUSTED_ORIGINS |
empty | Extra browser origins allowed to save changes. Use for a public domain or an unusual port. |
Basic Auth is short for HTTP Basic Authentication, a simple username and password prompt. Its credentials are only encoded, not encrypted, so always pair it with HTTPS when your server faces the public internet. HTTPS is the secure, encrypted version of HTTP. To turn it on directly, set both SSL_CERT and SSL_KEY, or put a reverse proxy in front of Marinara.
To let other devices reach the server at all, the server must bind to a reachable interface. Set HOST=0.0.0.0. The shell launchers do this for you, but pnpm start binds to loopback only.
Phones, tablets, Tailscale peers, and other computers can continue to connect by the server's IP address without adding it to TRUSTED_HOSTS. If you publish Marinara at a public or reverse-proxy hostname, add that exact name, for example TRUSTED_HOSTS=chat.example.com. Names already present in CSRF_TRUSTED_ORIGINS or CORS_ORIGINS are also accepted for compatibility. This Host check prevents a public website's DNS name from being rebound to Marinara's loopback address.
Storage settings control where your local data lives. Your data includes chats, characters, avatars, and generated media.
| Variable | Default | What it does |
|---|---|---|
DATA_DIR |
packages/server/data |
Root folder for all user data. Docker images set /app/data. |
FILE_STORAGE_DIR |
the storage folder inside DATA_DIR |
Overrides the file-storage folder. |
ENCRYPTION_KEY |
empty | Key used to encrypt saved API keys. Generate one with the command below. |
Marinara keeps your data as plain JSON files. This makes backups easy to copy and inspect.
To generate an encryption key, run this command and paste the result into ENCRYPTION_KEY:
openssl rand -hex 32
To learn what each data folder holds, see Where Your Data Is Stored.
Logging controls how much detail the server prints to its console. The main control is LOG_LEVEL. The server hides anything below the level you pick.
| Level | What it shows |
|---|---|
error |
Only serious, unrecoverable failures. |
warn |
Errors plus non-fatal warnings. This is the default. |
info |
Warnings plus startup and per-request logs. |
debug |
Everything, including full prompts and model replies. Very verbose. |
Recommended choices:
- Keep the default
warnfor normal use. It is quiet and shows only real problems. - Use
infowhen you want to see requests and milestones without flooding the console. - Use
debugwhen you need to see the exact prompt sent to the model and the reply. Expect a lot of output.
To read prompt and connection details without the routine request logs, set a preset instead of a level:
LOG_PRESET=prompt-connections
That preset shows the same prompt and model detail as debug, but hides repeated request lines like GET /api/chats. To silence only those routine request lines while keeping your current level, set this and restart:
LOG_DISABLE_REQUEST_LOGGING=true
Browser logging is separate and is not controlled by LOG_LEVEL.
A timeout is the longest time the server waits for a slow job before giving up. Media jobs like image and video generation can be slow, so their timeouts are generous by default. All timeout values are in milliseconds unless the name says otherwise.
| Variable | Default | What it does |
|---|---|---|
CHAT_GENERATION_TIMEOUT_MS |
300000 (5 minutes) |
Provider headers/time-to-first-token and inter-chunk timeout for ordinary Conversation, Roleplay, and Game generations, and the time-to-first-byte budget for background generation that has no timeout of its own (Noodle timeline refresh, Noodler replies). Valid range: 10000-3600000. It does not change Agent, media, embedding, or tool timeouts. |
AGENT_CALL_TIMEOUT_MS |
300000 (5 minutes) |
Total-duration cap for one agent LLM call (trackers, HTML reformatter, and other agents), applied even while the response is still streaming. Raise it for slow local models that need longer than 5 minutes per agent pass. Valid range: 10000-3600000. The Illustrator keeps at least its built-in 30-minute budget. |
GAME_DYNAMIC_IMAGE_PROMPT_TIMEOUT_MS |
45000 (45 seconds) |
Total-duration cap for the model call that turns the current Game scene into a dynamic image prompt. Raise it for slower local models. Valid range: 10000-3600000. |
EMBEDDING_TIMEOUT_MS |
300000 (5 minutes) |
Time allowed for one embedding request. Higher helps slow local embedding servers. |
IMAGE_GEN_TIMEOUT_MS |
1800000 (30 minutes) |
Time allowed for one image generation request. |
VIDEO_GEN_TIMEOUT_MS |
1800000 (30 minutes) |
Time allowed for one scene video generation request, including local ComfyUI video workflows. |
VIDEO_GEN_MAX_RESPONSE_BYTES |
167772160 (160 MiB) |
Largest scene video download the server will accept. |
COMFYUI_GEN_TIMEOUT |
2400 (40 minutes, in seconds) |
Time allowed for one ComfyUI image workflow after it is queued. |
SPRITE_GENERATION_TIMEOUT_MS |
falls back to IMAGE_GEN_TIMEOUT_MS |
Time allowed for one AI sprite generation job. |
CUSTOM_TOOL_TIMEOUT_MS |
60000 (1 minute) |
Time allowed for one custom tool call. |
MAX_TOOL_ROUNDS |
100 |
Most tool-call rounds before the model must give a final answer. |
The image, video, sprite, and ComfyUI timeouts are locked in at startup, so a change to them needs a restart. Chat-generation, agent, dynamic Game image-prompt, embedding, and custom-tool timeouts take effect on the next request or agent run, with no restart. Invalid, zero, negative, or out-of-range values for the validated chat, agent, or dynamic Game image-prompt timeouts log a warning and safely use their documented defaults. Raise a media timeout when large or high-quality jobs fail partway through. To learn more about video jobs, see Scene Video.
Some actions are destructive or high-risk, so they need an extra secret on top of the normal access checks. Examples are backups, clearing data, applying updates, and installing themes.
Set a long, random value for ADMIN_SECRET on the server:
ADMIN_SECRET=replace-this-with-a-long-random-secret
On the machine running the server (loopback), these actions usually work without the secret. From another device, the app must send the secret. Paste the same value into the app under Settings, then Advanced, then Admin Access. After that, the app sends it for you.
Related privileged settings:
| Variable | Default | What it does |
|---|---|---|
ADMIN_SECRET |
empty | Shared secret required for privileged actions from remote devices. |
MARINARA_REQUIRE_ADMIN_SECRET_ON_LOOPBACK |
false |
When true, requires the secret even on the local machine. |
UPDATES_APPLY_ENABLED |
false |
Allows the browser to apply ordinary same-channel updates. A deliberate release-channel switch from a browser on the server machine works without this flag. Git-based installs only. |
UPDATES_ALLOW_REMOTE_APPLY |
false |
Allows a remote device to apply updates, with a valid secret. |
HAPTICS_ALLOW_REMOTE |
false |
Allows haptic device actions from a remote device, with a valid secret. |
CUSTOM_TOOL_SCRIPT_ENABLED |
false |
Enables custom script tools. Keep off for untrusted or imported tools. |
ENABLE_CUSTOM_AGENT_REPOS |
false |
Enables manual GitHub agent-repository preview and sync in Agents Manager. Third-party agents are unvetted and require explicit confirmation before import or update. |
ENABLE_EXTERNAL_EXTENSIONS |
false |
First of two gates for third-party extension imports. The user must also opt in under Settings → Advanced → Danger Zone. |
IMPORT_ALLOWED_ROOTS |
empty | Filesystem folders that bulk import may read without a picker token. |
PROFILE_EXPORT_JSON_LIMIT_BYTES |
268435456 (256 MiB) |
Largest single JSON profile export the server will build. |
If ADMIN_SECRET is not set on the server, privileged actions fail from any device except the local machine. The error tells you to set the secret and paste it into Admin Access.
By default, outbound requests to providers, image services, and webhooks refuse to reach private or local addresses. This blocks a class of attack called SSRF (server-side request forgery), where a request is tricked into reaching an internal address. Loopback provider addresses stay allowed so local model servers keep working.
Turn on only the switch you need for a self-hosted service on another private-network machine.
| Variable | Default | What it does |
|---|---|---|
PROVIDER_LOCAL_URLS_ENABLED |
false |
Allows AI provider URLs to reach private or LAN addresses. On by default on Android. |
IMAGE_LOCAL_URLS_ENABLED |
false |
Allows image provider URLs to reach private or LAN addresses. Private generated-image result URLs must still match the configured provider's exact origin. |
TTS_LOCAL_URLS_ENABLED |
false |
Allows text-to-speech URLs to reach private or LAN addresses. |
DEEPLX_LOCAL_URLS_ENABLED |
false |
Allows DeepLX translation URLs to reach private or LAN addresses. |
WEBHOOK_LOCAL_URLS_ENABLED |
false |
Allows custom tool webhooks to reach private or LAN addresses. |
To connect a local or self-hosted model, see Connecting a Local or Self-Hosted Model.
This section lists the remaining settings, grouped by purpose. The tables above already cover access control, storage, logging, timeouts, privileged actions, and local address opt-ins.
| Variable | Default | What it does |
|---|---|---|
PORT |
7860 |
The port the server listens on. Keep Android, Docker, and Termux on the same value. |
HOST |
127.0.0.1 (0.0.0.0 in the shell launchers) |
The network interface to bind. Use 0.0.0.0 for LAN access. |
AUTO_OPEN_BROWSER |
true |
Whether the shell launchers open the app URL for you. Set false to stop this. |
AUTO_UPDATE_ENABLED |
true |
Whether Git-based Windows, macOS/Linux, and Termux launchers fetch and apply Engine updates before startup. Set false for a persistent opt-out; this takes effect on the next launch. The launcher still performs a read-only check for newer published releases and prints a download reminder when one is available, while manual checks, in-app apply, package updates, and model updates remain available. Use --skip-update to skip both launcher checks for one start. |
MARINARA_ENV_FILE |
project-root .env |
Optional path override for the .env file. Set it before startup. |
TZ |
system default | Host fallback timezone for server-side jobs. Conversation schedules use the global timezone selected in their schedule controls when one has been saved. Leave TZ unset to inherit the host timezone; an empty TZ= is also treated as unset. |
CORS_ORIGINS |
http://localhost:5173,http://127.0.0.1:5173 |
Browser origins allowed to make cross-origin requests. |
AUTO_CREATE_DEFAULT_CONNECTION |
true |
Legacy flag. Current builds bundle no starter key, so this creates nothing. Add your own connection in the app. |
AUTO_CREATE_DEFAULT_CONNECTION is kept only for older installs. New builds no longer ship a bundled starter connection, so leaving it on does nothing. To start chatting, add a connection under Connecting to an AI Provider.
Conversation schedule controls default to the timezone reported by the browser or app device. Schedule timezone can be changed during Conversation setup, in Conversation Chat Settings, or in the character schedule editor. The selected IANA timezone is one global preference shared by every Conversation chat and synced to other Marinara clients connected to the same server.
| Variable | Default | What it does |
|---|---|---|
FFMPEG_PATH |
empty | Path to an ffmpeg program. Used for animated expression GIFs. Falls back to ffmpeg on your PATH. |
SPRITE_ANIMATED_FFMPEG_TIMEOUT_MS |
180000 (3 minutes) |
Time allowed to convert one animated expression clip. |
SPRITE_BACKGROUND_REMOVAL_ENGINE |
auto |
Sprite cleanup engine. auto tries adaptive matte cleanup before the optional AI fallback; builtin keeps only the matte path; backgroundremover forces the AI tool. |
BACKGROUNDREMOVER_AUTO_INSTALL |
false |
When true, installs the optional AI background remover on launch. |
BACKGROUNDREMOVER_COMMAND |
empty | Path to a system backgroundremover program. |
BACKGROUNDREMOVER_PYTHON |
empty | Path to a Python program where backgroundremover is installed. |
BACKGROUNDREMOVER_TIMEOUT_MS |
600000 (10 minutes) |
Time allowed for one AI background-removal call. |
Scene video providers are set up as connections inside the app, not as environment variables. The settings below only tune the underlying jobs. All values are in milliseconds.
| Variable | Default | What it does |
|---|---|---|
GOOGLE_VEO_VIDEO_POLL_INTERVAL_MS |
10000 |
How often the server checks a Google Veo job. |
XAI_VIDEO_POLL_INTERVAL_MS |
5000 |
How often the server checks an xAI Imagine job. |
OPENROUTER_VIDEO_POLL_INTERVAL_MS |
10000 |
How often the server checks an OpenRouter video job. |
SEEDANCE_VIDEO_POLL_INTERVAL_MS |
10000 |
How often the server checks a Seedance job. |
VIDEO_REFERENCE_PUBLIC_BASE_URL |
empty | Public HTTPS address of this server, used when a provider must fetch a reference image by URL. |
| Variable | Default | What it does |
|---|---|---|
DOCS_I18N_BASE_URL |
official docs-i18n branch |
Where translated documentation packs download from (Settings → General → Documentation Language). Must be a public https:// host; forks and mirrors can point it at their own copy of the docs-i18n branch. |
GIPHY_API_KEY |
empty | Giphy key for GIF search in Conversation mode. Search is off when unset. |
INTIFACE_URL |
ws://127.0.0.1:12345 |
Default address for the Intiface haptic app. |
SPOTIFY_REDIRECT_URI |
derived from request | Override for the Spotify login callback URL. Set it when TLS is handled upstream. |
MARI_WIKI_CONTENT_MAX_BYTES |
50000 |
Largest wiki page content Professor Mari reads before trimming. |
MARI_WIKI_REQUEST_TIMEOUT_MS |
30000 |
Time allowed for one wiki request by Professor Mari. |
MARI_WIKI_CACHE_TTL_MS |
300000 |
How long Professor Mari caches a wiki read. |
SIDECAR_RUNTIME_INSTALL_ENABLED |
false (the Windows launcher sets true) |
Allows installing the local model runtime without an admin header on loopback. |
SSL_CERT |
empty | Path to a TLS certificate. See Access control above. |
SSL_KEY |
empty | Path to a TLS private key. See Access control above. |
For a Giphy key, note that GIF search stays unavailable until you set GIPHY_API_KEY and restart. For the built-in local model, see Local Model Setup.