Nodaro Docs
DocumentationNode ReferenceModelsAI Agents (MCP)DevelopersSelf-hostingResearch

Reverse proxy and HTTPS

Serve a self-hosted Nodaro on your own domain over HTTPS, with nginx or Caddy in front of port 3000, then set PUBLIC_URL and CORS_ORIGIN and restart the app.

A reverse proxy puts your self-hosted Nodaro on a real domain with HTTPS. The Nodaro container already runs a web server, Caddy, on port 3000, so you add one TLS-terminating proxy in front of that port and tell Nodaro its public address. Both nginx and Caddy work.

What port 3000 serves

The web server inside the container puts everything on one origin:

PathWhat answers
/The editor and the apps, as static files
/v1/*The API, which listens on port 9000 inside the container
/storage/*The media in the bundled MinIO
/supabase/*The bundled sign-in and data API
/config.jsThe runtime settings the browser reads before the app starts

Your proxy only needs to forward every request to port 3000.

Option A: nginx

Use this when you already run nginx or another proxy.

server {
  listen 443 ssl http2;
  server_name nodaro.example.com;
  ssl_certificate     /etc/letsencrypt/live/nodaro.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/nodaro.example.com/privkey.pem;

  client_max_body_size 100M;
  proxy_buffering off;          # important for SSE

  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}
  • client_max_body_size 100M lets uploads through.
  • proxy_buffering off keeps Server-Sent Events flowing. Nodaro streams text output and run progress with them.

Option B: Caddy on the host

Caddy gets a Let's Encrypt certificate for you:

nodaro.example.com {
  reverse_proxy 127.0.0.1:3000 {
    flush_interval -1
  }
}

Open ports 80 and 443, and point the domain's A or AAAA records at the host. flush_interval -1 keeps Server-Sent Events flowing.

Tell Nodaro its public address

After either option, set these in .env:

PUBLIC_URL=https://nodaro.example.com
CORS_ORIGIN=https://nodaro.example.com
# On the bundled MinIO, serve the media from the same domain:
R2_PUBLIC_URL=https://nodaro.example.com/storage/nodaro-assets

Then apply them:

docker compose -f docker-compose.community.yml up -d

No rebuild is needed. At boot, the container writes the public URL, the browser's sign-in address and the anon key into /config.js, and the browser reads that file before the app starts.

PUBLIC_URL is the address of the install everywhere: in sign-in and OAuth callbacks, in media URLs and in the CORS check. http://localhost:3000 and PUBLIC_URL are always allowed as browser origins.

Serve more than one hostname

  • Extra origins. List them in CORS_ORIGIN, comma-separated, for example CORS_ORIGIN=https://nodaro.example.com,http://192.168.1.20:3000.
  • Streams on the visitor's hostname. When the API shares the app's origin, as in the default container, set PUBLIC_URL_SAME_ORIGIN=true. /config.js then gives the browser the API address / instead of PUBLIC_URL, so streams stay on the hostname the visitor opened. Only the exact value true enables it. Leave it unset when the API really lives on another host.

The compose file does not pass PUBLIC_URL_SAME_ORIGIN from .env. Add it under environment: of the nodaro service. See Configuration.

Change the port

To serve the app on another host port, change the host side of the mapping in docker-compose.community.yml, for example "3001:3000". Then set PUBLIC_URL to match, such as http://localhost:3001.

How client addresses are recorded

The web server in the container accepts X-Forwarded-* headers only from proxies on private addresses: 127.0.0.1/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fd00::/8 and ::1.

  • From a trusted proxy, it reduces X-Forwarded-For to one client address: the rightmost entry that is not itself a trusted proxy. An address that a client wrote into the header is skipped.
  • From a proxy on a public address, it replaces those headers with the values it observed itself.

On an intranet, the same rule skips your users' own addresses when those are private too. A client on the LAN can then choose the address that the API records, for example for rate limits.

The embedded editors on your domain

The Edit video action opens the hosted video editor at freecut.nodaro.ai, which allows embedding only from http://localhost:3000. On any other origin, such as a domain or a LAN address, the browser refuses to embed it, and the panel explains why. Either open an issue on GitHub with the URL you serve, or run your own editor and set FREECUT_URL. See Configuration.

MCP needs its own hostname

MCP clients must reach the API on port 9000 directly: the web server on port 3000 refuses MCP requests. See MCP.

Frequently asked questions

Last updated on

On this page