Remote Access: Login and IP Allowlist

This guide explains how to reach Marinara Engine from another device, such as your phone, a laptop, or a Docker container. It covers the main option, a login. It also covers the IP allowlist, login proxies, HTTPS, Admin Access, and the "save blocked" CSRF message. Almost every setting here lives in the server .env file, not in the app.

A quick word list used throughout this guide:

Connect from anywhere with Tailscale

Tailscale is a private network for your own devices. Use it to open Marinara on your phone or laptop, at home or away, without opening ports on your router or putting Marinara on the public internet. Marinara trusts devices that connect straight over Tailscale, so you do not need to set a login.

  1. Install Tailscale on the computer that runs Marinara and on your other device. Get it from tailscale.com/download.
  2. Sign in to Tailscale with the same account on both devices.
  3. Start Marinara with start.sh, start.bat, start-termux.sh, or the Windows installer. These let other devices connect. If you run pnpm start instead, first add HOST=0.0.0.0 to your .env file (see Where the .env file lives).
  4. Find the Tailscale address of the Marinara computer. Open the Tailscale app on it, or run tailscale ip -4. The address starts with 100., for example 100.101.102.103.
  5. On your other device, turn Tailscale on and open http://100.101.102.103:7860 in a browser. Use your own address, and your PORT if you changed it. Marinara opens without a sign-in page.

If the page does not load, check that Tailscale is on for both devices and that the server listens on HOST=0.0.0.0.

Note: This does not work for Docker installs. Tailscale traffic reaches the container through the Docker gateway, so Marinara cannot see that it came from Tailscale. Set a login first (Option 1), then publish the port beyond 127.0.0.1.

Backups, imports, updates, and other risky actions still need the admin secret from a remote device. See Admin Access and privileged actions.

What Marinara blocks by default

To protect your data, a fresh Marinara install refuses connections from other devices until you set up access control. By default, only three kinds of client are trusted:

  1. Loopback (127.0.0.1 or ::1), the machine running the server itself. Visitors that a reverse proxy or tunnel on the same machine passes along do not count; see Reverse proxies and tunnels on the same computer.
  2. Tailscale devices in your tailnet. Tailscale is a private network tool, and its addresses use the 100.64.0.0/10 range.
  3. Docker clients on the same host. Marinara recognizes the usual 172.16.0.0/12 bridge range and the container's exact default gateway, which also covers Docker Desktop and custom address pools.

Everything else, such as your phone on the same Wi-Fi or a public-internet client, is blocked until you set a login. A blocked device that opens Marinara in a browser sees a dark setup page. Its title reads This Marinara Engine install needs a login before remote devices can connect. The page has a form for the one-time setup code from the server log, so you can set the login from that device.

If you do nothing and never set a password, Marinara stays locked to those three trusted sources. That is the safe default.

Where the .env file lives

All access settings live in your .env file in the project root, next to package.json. If you do not have one yet, copy the example:

cp .env.example .env

Open .env with any text editor. Most access settings, including Basic Auth, the IP allowlist, the admin secret, and CSRF origins, apply within a couple of seconds without a restart. A few low-level settings still need a restart, including PORT, HOST, and the HTTPS certificate paths.

Other devices may fail to reach the server at all, with a timeout rather than a 403. In that case, the server may only be listening on the local machine. Set the server to listen on every network interface:

HOST=0.0.0.0

The shell launchers (start.bat, start.sh) set HOST=0.0.0.0 for you. Running pnpm start directly does not.

Default bind address by how you start Marinara:

Start methodHOST default
pnpm start, start-local.bat127.0.0.1
start.sh, start.bat, Windows installer, start-termux.sh0.0.0.0
Docker image0.0.0.0 inside the container
docker-compose.ymlPublishes the port on 127.0.0.1 only

Which option should you pick

Read these in order and stop at the first one that matches you.

  1. You only connect directly over Tailscale, or only from Docker containers on the same host. With pnpm start, direct Tailscale clients skip the login; set HOST=0.0.0.0 so they can reach the server. The default Compose file publishes on 127.0.0.1 only. If you publish the port beyond loopback so a Tailscale device can reach it, set a login first. Docker then asks every device for that login, Tailscale devices included, because Docker sees them at the gateway address. See Tailscale and Docker bypass.
  2. You want to reach Marinara from a phone, tablet, or laptop on your home Wi-Fi. Set a login (Option 1 below).
  3. You are exposing Marinara to the public internet. Set a login and use HTTPS.
  4. A login proxy on your network, such as tinyauth, Authelia, or Authentik, already asks every visitor to log in. Read Login proxies (forward auth).

A login is the most flexible choice. It works from any IP, needs no per-device setup, and each browser stays signed in.

Option 1: Login (recommended)

A login is a username and password that every remote device needs. You can set it in three ways:

BASIC_AUTH_USER=alice
BASIC_AUTH_PASS=correct-horse-battery-staple

A login in .env wins over a login set in the app, and you change it only in .env. Save .env; the change applies within a couple of seconds, with no restart.

Pick a strong, unique password. The app asks for at least 10 characters. You can generate a random one:

openssl rand -base64 24

Then follow these steps from the remote device.

  1. Open Marinara in your browser using the server's address, for example http://192.168.1.50:7860.
  2. Sign in on the sign-in page with your username and password.
  3. You should see the app load. The browser stays signed in for up to a year, until you sign it out. Scripts and API clients send the username and password with every request (Basic Auth).

To sign in another device without typing the password, use a pairing code. On a device that is signed in, open Settings → Advanced → Access and choose Add a device. Then type the code on the sign-in page of the new device. The code works once, for a few minutes. An expired code gets its own message; make a new one. Under Signed-in devices, you see each device with its sign-in method and the date it first signed in. You can sign out any device, including the one you use. You can also change or remove the password. For that, Marinara asks for the current password, unless you work on the computer that runs Marinara or have set ADMIN_SECRET.

If you forget the password, run node scripts/reset-login.mjs in the Marinara folder on the computer that runs Marinara. In Docker, run docker exec <container> node scripts/reset-login.mjs. It removes the login set in the app and signs out every browser. If the server listens beyond loopback, it then prints a new setup code. Only if that command does not work, delete .remote-login.json and .remote-sessions.json from the data folder instead. With the default /app/data, run docker exec <container> rm -f /app/data/.remote-login.json /app/data/.remote-sessions.json; if you changed DATA_DIR, use that folder. A login in .env you change in .env.

Headless server with no browser? A native install on a headless server listens on loopback only and prints no setup code. On your own computer, run ssh -L 7860:127.0.0.1:7860 user@server. Then open http://localhost:7860 there and use Settings.

After 10 different wrong passwords or codes within 15 minutes, Marinara refuses sign-ins from that address with 429 Too Many Requests until the 15 minutes are up. A correct password before then clears the count. Visitors that a reverse proxy or tunnel on the host passes along share one count, and one of them signing in does not clear it. Restarting the server also clears it. A browser that has signed in before still gets in with the correct password, so other people's wrong passwords cannot lock you out of a device you already use. The sign-in page tells you how many minutes to wait. If you forget the password, the sign-in page has a Forgot the password? hint.

Browsers sign in on the /login page. BASIC_AUTH_REALM changes the realm in the Basic Auth challenge for scripts and API clients; it does not change the sign-in page.

Some clients skip the password even when Basic Auth is on:

Important: Basic Auth only encodes the password. It does not encrypt it. Anyone watching an unencrypted connection can read it. If you expose Marinara to the public internet, pair Basic Auth with HTTPS (see below).

Option 2: IP allowlist

The IP allowlist lets specific addresses in without a password. It is a good fit when your devices have stable IP addresses. Set a comma-separated list of addresses or ranges:

IP_ALLOWLIST=192.168.1.0/24,203.0.113.42

The /24 in the example is CIDR notation. CIDR is a short way to write a whole range of addresses in one entry. For example, 192.168.1.0/24 covers every address from 192.168.1.0 to 192.168.1.255. A bare address with no slash, like 203.0.113.42, matches only that one device.

A broad public range, such as 0.0.0.0/0, can let internet clients in without a login. List only addresses you trust.

How the IP allowlist behaves:

The allowlist cannot create a mixed setup where listed devices skip the password and everyone else logs in. If you want other devices to log in with a password, leave IP_ALLOWLIST unset and use Basic Auth alone.

You can turn enforcement off for a while without deleting your list. This is handy when troubleshooting from a new IP. Set the enable flag to false:

IP_ALLOWLIST_ENABLED=false

Private-network bypass (reference)

A few flags drop the lockdown for a trusted private network. They are for special setups. A login is the better choice. See Access Control for the flags and their risks.

Tailscale and Docker bypass

By default, Marinara trusts a device that connects straight over your Tailscale network. It also trusts a Docker container's own host. A login, an IP allowlist, or TRUSTED_AUTH_PROXY stops the automatic Docker trust, so every device then needs its login or its place on the allowlist. For the flags that change this, see Access Control.

Docker Desktop, OrbStack and rootless Docker show every device that uses a published port at the Docker gateway address. If you publish the port on all interfaces (-p 7860:7860) and set no login, anyone on your network gets in. Keep the port on 127.0.0.1 or set a login first. The server log warns about this at startup.

Reverse proxies and tunnels on the same computer

A reverse proxy or tunnel on the machine running Marinara, such as nginx, Caddy, Traefik, Cloudflare Tunnel, Tailscale Serve or Funnel, or ngrok, connects to Marinara from loopback. Marinara recognizes this from the forwarding headers the proxy adds: Forwarded, X-Forwarded-For, X-Real-IP, X-Forwarded-Host, X-Forwarded-Proto, CF-Connecting-IP, True-Client-IP, the Tailscale identity headers, or Via. Marinara also recognizes a request that arrives over HTTP/1.0, because browsers never use it but a default nginx proxy_pass does. Marinara also recognizes a request whose Host names another computer, such as a domain or a LAN address, even when the proxy adds no headers. It then treats each request as the remote visitor it is:

Your own browser on that machine, opened at http://127.0.0.1:7860 or http://localhost:7860, stays passwordless.

Caddy, Traefik, Cloudflare Tunnel, Tailscale, and ngrok add forwarding headers by default. For nginx, this line is still recommended in the location block:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Tools that forward raw connections, such as socat or ssh -R, add no headers, so their traffic looks local. Do not use them to publish Marinara.

If your proxy already makes every visitor log in before it forwards anything, see Login proxies (forward auth).

Login proxies (forward auth)

A forward-auth proxy, such as tinyauth, Authelia, Authentik, or oauth2-proxy, asks every visitor to log in before it forwards the request. If such a proxy is the only way to reach Marinara, you can skip the second Marinara login. Do not set this without such a proxy.

Put the address of the proxy in .env:

TRUSTED_AUTH_PROXY=192.168.1.5

Marinara then lets in every request whose connection comes from that address. Marinara does not ask for its own login for them, and it prints no setup code. Marinara checks the address of the connection itself. It ignores forwarding headers such as X-Forwarded-For for this decision. Requests from the proxy also pass IP_ALLOWLIST, because the proxy decides who gets in. They do not count as local, so privileged actions still need the admin secret. Settings → Advanced → Access shows Trusted login proxy: Set while the setting is active.

Before you set it:

When TRUSTED_AUTH_PROXY is set, Marinara also stops the automatic Docker trust. Direct visitors who are not the proxy need the Marinara login or the IP allowlist. Marinara then also shows no "no login yet" banner.

If the proxy runs on the same computer as Marinara, use TRUSTED_AUTH_PROXY=127.0.0.1 and point the proxy at 127.0.0.1:7860, not localhost:7860. A proxy that dials localhost can connect from ::1, which does not match 127.0.0.1. Then every other proxy or tunnel on that computer is trusted too, so use it only when the login proxy is the only one. This does not give proxied visitors the local machine's access.

A Caddy example, with Marinara at 192.168.1.20:7860:

marinara.example.com {
  forward_auth tinyauth:3000 {
    uri /api/auth/caddy
  }
  reverse_proxy 192.168.1.20:7860
}

For Authelia, use forward_auth authelia:9091 with uri /api/authz/forward-auth. Authentik also serves its own sign-in pages through the proxy. Put its lines in a route block so that Caddy keeps their order and the outpost path does not go through forward auth itself:

marinara.example.com {
  route {
    reverse_proxy /outpost.goauthentik.io/* authentik-server:9000
    forward_auth authentik-server:9000 {
      uri /outpost.goauthentik.io/auth/caddy
    }
    reverse_proxy 192.168.1.20:7860
  }
}

For Traefik and nginx, follow the forward-auth guide of your proxy. Marinara needs nothing more than the address of the proxy.

Serving over HTTPS

HTTPS encrypts the connection using TLS. TLS is the encryption that turns a plain http address into a secure https one. Always use HTTPS for any install reachable outside a fully trusted private network, especially with Basic Auth.

You have two ways to add it.

  1. Built-in TLS. Point the server at a certificate and private key file:
SSL_CERT=/path/to/cert.pem
SSL_KEY=/path/to/key.pem
  1. Reverse proxy. Put Marinara behind nginx, Caddy, Traefik, or a Cloudflare Tunnel. The proxy handles the HTTPS part and forwards to Marinara over plain HTTP on the same machine. Read Reverse proxies and tunnels on the same computer first.

You need a certificate and key before setting SSL_CERT and SSL_KEY. You can create one with a tool like mkcert for local use, or certbot for a public domain. If the files are missing or unreadable, the server stops on startup and names the exact paths it tried.

Admin Access and privileged actions

Some actions are extra sensitive: clearing data, creating or downloading backups, importing and exporting profiles, installing themes and agents, and installing the Local Model runtime. These need a separate shared secret called the admin secret, on top of whichever access option you chose above.

On the loopback machine, these actions usually work with no admin secret. From a remote device, you need to set the secret up. You can make it in the app or set it in .env.

Installed agents, such as the ones on the Home screen, do not need the admin secret. A device that signed in with the login can use them. Installing, updating and removing an agent still need it.

Make the admin secret in the app

  1. On the computer that runs Marinara, open Settings, then the Advanced tab, then the Admin Access section.
  2. Click Make admin secret. Marinara shows the new secret once and saves it in this browser.
  3. On each remote device, open Settings → Advanced → Admin Access, type the secret into the box, then click Save. Case, spaces and dashes do not matter.

A device that already has the secret can also make it. A device that is only signed in with the login cannot. This stops a stolen sign-in from taking over the risky actions.

With Docker, your browser reaches Marinara through the Docker network, so Marinara does not treat it as the same computer. Set the first secret in .env instead (see below).

To change the secret, click Replace admin secret. The old secret stops working on every device. To turn it off, click Remove admin secret. Marinara keeps only a fingerprint of the secret, in .admin-secret.json in the data folder. Deleting that file also removes the secret.

Set the admin secret in .env

  1. In .env, set a strong random value and save. It applies within a couple of seconds, no restart.
ADMIN_SECRET=some-long-random-string
  1. On the remote device, open Settings → Advanced → Admin Access.
  2. Paste the same value into the box (its placeholder reads ADMIN_SECRET), then click Save.
  3. You should see the message Admin secret saved for this browser.

ADMIN_SECRET in .env wins over a secret made in the app. While it is set, Admin Access says so and you change the secret in .env only.

Good to know

If a privileged action fails on a remote device, Marinara shows an error message with two fixes. One fix is to open the app through localhost. The other is to set up the admin secret as described above.

Why is my save blocked (CSRF)

CSRF stands for cross-site request forgery. It is a protection that stops another website you have open from quietly making changes in Marinara without your permission. It runs automatically. There is no setting to turn it on.

Sometimes CSRF blocks your own saves. This usually happens when you reach Marinara through a public domain name or an unusual port that the server does not yet trust. Two things tell you when this happens.

To fix it, add your address to the trusted list in .env:

CSRF_TRUSTED_ORIGINS=https://chat.example.com,http://203.0.113.10:7831

When you use a public or reverse-proxy domain, also allow the hostname:

TRUSTED_HOSTS=chat.example.com

Direct LAN, Tailscale, IPv4, and IPv6 addresses do not need TRUSTED_HOSTS. Local .local/.home.arpa names and single-label machine names are accepted automatically. An exact hostname already listed in CSRF_TRUSTED_ORIGINS is accepted too.

Loopback, normal LAN addresses, Tailscale (100.64.0.0/10), and Docker bridge (172.16.0.0/12) origins are trusted automatically. You only need to list public IP addresses and domain names. The change takes effect within a couple of seconds, no restart needed.

A note on blocked local providers

Say you connect Marinara to a local AI provider, for example one running on your own machine. The request may be refused with a message about a "private, loopback, metadata, or reserved IP range". That is a different safety check called SSRF protection. SSRF stands for server-side request forgery. It stops the server from calling private addresses unless you allow it. The error names the exact .env variable to set, such as PROVIDER_LOCAL_URLS_ENABLED. See Server Configuration Reference for the full list.

Access from a phone or tablet

To open Marinara from a phone or tablet on the same network:

  1. Make sure the server listens on all interfaces with HOST=0.0.0.0 in .env.
  2. Set a login (Option 1 above). From the phone, you can do it on the page that blocks you, with the setup code from the server log.
  3. Find the server machine's local IP (for example 192.168.1.50).
  4. On the phone, open http://192.168.1.50:7860 in a browser. The default port is 7860.
  5. Sign in with your username and password.

If the page does not load at all, the server may not be reachable. Check HOST=0.0.0.0 and the PORT value. If you get a 403 instead, your device is reachable but not yet allowed in. Recheck your chosen option above.

Locked out after updating

Newer versions ask for a login when a visit comes through a reverse proxy, a tunnel, Tailscale Serve, or Tailscale Funnel. Older versions let these visits in without a password. If you now see a login page, a setup page, or a 403, this is why.

Upgrade note: if you use Tailscale Serve, or another proxy on the same computer, and never set a password, your other devices are locked out after the update. A proxy on the same computer usually talks to a server that listens on loopback only, so the server prints no setup code. Set the login on the computer that runs Marinara instead.

To get back in:

  1. Open Marinara on the computer that runs it, at http://localhost:7860 (or your own PORT). Local use needs no password. On a headless server, first run ssh -L 7860:127.0.0.1:7860 user@server on your own computer, then open http://localhost:7860 there. In Docker, this works only while no login and no IP_ALLOWLIST are set. Otherwise type the setup code from the server log (docker logs <container> 2>&1 | grep "setup code") on the page that blocks you or in Settings → Advanced → Access, or use a browser that is still signed in.
  2. Go to Settings → Advanced → Access and set a username and password under Remote login. Or open the .env file on that computer and set BASIC_AUTH_USER and BASIC_AUTH_PASS. If IP_ALLOWLIST is set, add your other device's address to it or remove it, because a device that is not on the list gets a 403 even with a login.
  3. Sign in from your other device with that username and password.

Search engines

Marinara asks search engines not to index its pages (X-Robots-Tag: noindex, nofollow). This does not protect your installation and does not remove pages that are already indexed. Use the access controls above to keep your installation private.

Related guides