Nodaro Docs
DocumentationNode ReferenceModelsTemplatesAI Agents (MCP)DevelopersSelf-hostingResearchCompare

Configuration

Every environment variable of a self-hosted Nodaro, grouped by purpose, with its default, whether .env can set it on the compose stack, and what it changes.

This page is the configuration reference of a self-hosted Nodaro: every environment variable that operators set, grouped by purpose, with its default. On the bundled compose stack, every variable has a working default, so you set only what you want to change. Variables that apply only to Nodaro Cloud are not listed.

How to set a variable

On the compose stack, you set a variable in one of two places:

  • In .env, next to docker-compose.community.yml. This works for the variables that the compose file passes to the app. The tables below say Yes in the .env column for them.
  • In the compose file, for every other variable. Add it under environment: of the nodaro service:
  nodaro:
    environment:
      # ...the variables already listed...
      MCP_ENABLED: "true"
      MCP_PUBLIC_URL: https://mcp.nodaro.example.com

Then apply the change:

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

Nodaro reads its environment at boot. The only exception is the provider keys you paste on /setup, which apply at once. Outside the compose stack, set the variables in your platform's environment settings.

Required outside the compose stack: SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY and INTERNAL_ORCHESTRATOR_SECRET. Without them, the API refuses to start and lists the missing or invalid variables.

Instance

VariableDefault.envWhat it does
EDITIONcommunityNocommunity, business or cloud. The compose file fixes it to community. See Editions and surface profiles.
PUBLIC_URLhttp://localhost:3000YesThe install's public origin: sign-in and OAuth callbacks, media URLs and the CORS check.
PUBLIC_URL_SAME_ORIGINunsetNotrue gives the browser the API address / instead of PUBLIC_URL, so streams stay on the hostname the visitor opened. For installs that answer on several hostnames. Only the exact value true enables it.
CORS_ORIGINemptyYesExtra browser origins, comma-separated. PUBLIC_URL is always allowed. On these hosts, the SSO landing redirect is relative.
DEFAULT_LOCALEempty (the browser's language)YesThe language a new visitor starts in. See Default language.
NODARO_SURFACE_PROFILEempty (the full interface)YesNarrows the interface of a Business install. See Surface profiles.
NODARO_TUTORIAL_PACKSempty (built-in tutorials only)YesDirectories of extra tutorial packs, comma-separated. See Tutorial packs.
RUNTIME_ENVlocalNoNames the install when two installs share one database but not one Redis. See Scaling. On Railway, RAILWAY_ENVIRONMENT_NAME sets it. A webhook endpoint belongs to the install that created it, so renaming RUNTIME_ENV hides the endpoints created under the old name.
PORT, HOST8000, 0.0.0.0NoWhere the API listens. In the image, the API listens on 9000 behind the web server on 3000.
NODE_ENVdevelopmentNoproduction in every image.

Secrets

VariableDefault.envWhat it does
INTERNAL_ORCHESTRATOR_SECRETGenerated at boot on the compose stackYesAuthenticates the orchestrator to the API. At least 32 characters. Required outside the compose stack.
NODARO_ENCRYPTION_KEYGenerated on the first boot on the compose stack, and kept in the app-data volumeYesA 64-character hex key that encrypts stored credentials: pasted provider keys, social sign-in tokens, the HTTP credentials saved for Webhook Output and the signing secrets of webhook endpoints. Without it, registering a webhook endpoint answers 503 encryption_key_missing.
SOCIAL_ENCRYPTION_KEYemptyYesThe older name of NODARO_ENCRYPTION_KEY. Either works.

Generate a secret with openssl rand -hex 32. Back up the encryption key with the database: see Backups and restore.

Database and sign-in

VariableDefault.envWhat it does
SUPABASE_URLhttp://localhost:3000/supabase on the compose stackYesThe Supabase URL the API uses. Required outside the compose stack.
SUPABASE_SERVICE_ROLE_KEYA public default key on the compose stackYesThe service-role key the API uses. Required outside the compose stack.
SUPABASE_ANON_KEYA public default key on the compose stackYesThe anon key handed to the browser and the sign-in service.
FRONTEND_SUPABASE_URLPUBLIC_URL/supabase on the bundled stackYesThe Supabase URL the browser uses, written into /config.js at boot. Set it with a managed Supabase project.
SUPABASE_JWT_SECRETA public default secretYesThe secret that signs the anon and service-role keys on the bundled stack. Mint all three with node tools/generate-selfhost-keys.mjs.
POSTGRES_PASSWORDnodaro-postgres-pwYesThe bundled database's password. The internal role passwords follow it only on the database's first start.
DATABASE_URLpostgres://postgres:nodaro-postgres-pw@db:5432/postgresYesA direct Postgres URL, used only to apply migrations at boot. Keep its password in step with POSTGRES_PASSWORD.
RUN_MIGRATIONS_ON_BOOTfalse; true on the compose stackYesApplies supabase/migrations/ before the API starts. Set false with a managed Supabase project.
SUPABASE_MANAGED_PROXYunsetNotrue serves a managed Supabase project through the app's own origin. See Database.
SUPABASE_PROXY_UPSTREAMunsetNoThe managed project's origin, without a path, for the proxy above.

Queues

VariableDefault.envWhat it does
REDIS_URLredis://localhost:6379; redis://redis:6379 on the compose stackNoThe Redis for queues and shared caches. A cluster or Sentinel URL also works.

Storage

VariableDefault.envWhat it does
R2_ENDPOINThttp://minio:9000 on the compose stackYesThe S3 API URL of the store. On the compose stack, an empty value falls back to MinIO.
R2_FORCE_PATH_STYLEtrue on the compose stackYesPath-style addressing, which most self-hosted stores need.
R2_ACCOUNT_IDemptyYesThe Cloudflare account ID. Outside the compose stack, the R2 endpoint is derived from it when R2_ENDPOINT is unset.
R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEYThe bundled MinIO's credentialsYesThe storage credentials. On the compose stack, they are also the MinIO root credentials.
R2_BUCKET_NAMEnodaro-assets on the compose stackYesThe bucket. Outside the compose stack the default is scenenode-assets, so always set it.
R2_PUBLIC_URLhttp://localhost:3000/storage/nodaro-assets on the compose stackYesThe public URL of the bucket.
R2_REGIONautoYesThe S3 region. Set a real one for AWS, DigitalOcean Spaces (nyc3 and more) and a local Supabase (local).
STORAGE_OBJECT_ACLempty (no ACL header)YesA canned ACL on every uploaded object, for stores that refuse a bucket policy. See Install.
R2_PUBLIC_FALLBACK_DOMAINemptyNoA second public host for media, for example the raw r2.dev address next to a CDN domain.
R2_SHARED_WITH_RELAY_TARGETfalseNotrue only when R2_PUBLIC_URL names the same bucket that your Nodaro Cloud host writes to. See Connect to Nodaro Cloud.

Model providers

Each of these keys can also be pasted on /setup. A key set in the environment wins. See Provider keys.

VariableDefault.envWhat it powers
NODARO_API_KEYemptyYesNodaro Cloud as a provider, with a personal API token.
KIE_API_KEYemptyYesThe broadest coverage of media and text models.
KIE_API_BASE_URLThe provider's own API hostYesSends that provider's traffic through your own proxy. It also moves the Claude and Gemini text traffic that runs on KIE_API_KEY.
REPLICATE_API_TOKENemptyYesAn alternative catalog, including the Flux 2 models.
ANTHROPIC_API_KEYemptyYesClaude, directly, for the text nodes.
GEMINI_API_KEYemptyYesGemini, directly. See Gemini models.
ELEVENLABS_API_KEYemptyYesSpeech, voices and dubbing.
ELEVENLABS_BASE_URLhttps://api.elevenlabs.ioYesSends ElevenLabs traffic through your own proxy.
FAL_KEYemptyYesModels such as Sync Lipsync v3.
HEYGEN_API_KEYemptyYesAI Avatar and Cinematic Avatar.
BEEBLE_API_KEYemptyYesRelight & Switch.
APIFY_API_TOKENemptyYesWeb Scrape and Site Capture.
SITE_CAPTURE_ENABLEDonNofalse turns off the Site Capture node, POST /v1/site-capture and the capture_site MCP tool on this install.
HEYGEN_CATALOG_REFRESH_HOURS24NoHow often the shared list of HeyGen avatars and voices is refreshed.

Nodaro Cloud connection

VariableDefault.envWhat it does
NODARO_CLOUD_URLhttps://app.nodaro.aiYesThe Nodaro Cloud host that Connect nodaro.ai registers with, and where calls made with NODARO_API_KEY go. Change it only when your install belongs to a dedicated Nodaro deployment. Read at boot.

NODARO_API_KEY and R2_SHARED_WITH_RELAY_TARGET also belong to the connection. See Connect to Nodaro Cloud.

Workers and performance

VariableDefault.envWhat it does
MAX_CONCURRENT_NODES_PER_EXECUTION6, at most 20NoHow many nodes one workflow run may run at once.
VIDEO_WORKER_CONCURRENCY50NoParallel jobs in the media worker, which mostly waits on providers.
ORCHESTRATOR_CONCURRENCY20NoParallel jobs in the orchestrator.
RENDER_WORKER_CONCURRENCY2, at most 10NoParallel renders. Each render runs a headless Chrome.
REMOTION_CONCURRENCY2 for 3D scenes; half of the CPU cores for other rendersNoBrowser tabs per render. A value you set applies to both. Keep it low when several 3D jobs run: each WebGL tab adds threads and counts toward the container's process limit.
FFMPEG_CONCURRENCY4, at most 32Noffmpeg processes at once, across every video and audio editing node.
RAILWAY_DEPLOYMENT_DRAINING_SECONDSunsetNoOn Railway only: the time between the stop signal and the forced stop of a replaced container. The media worker drains for this long minus 5 seconds. Unset, it drains for 25 seconds.

Read Scaling before you raise these.

Editors

VariableDefault.envWhat it does
FREECUT_URLempty (the hosted editor)YesThe video editor behind Edit in NodarCut on the canvas and Edit on a video result in an app. off turns the editor off: the buttons stay, but no editor opens.
AUDIOMASS_URLempty (no audio editor)YesThe audio editor behind Edit on an audio result in an app. off behaves like empty.

See Video and audio editors.

Updates

VariableDefault.envWhat it does
NODARO_IMAGEghcr.io/nodaroai/nodaro-community:latestYesThe app image the compose file runs. Pin a release tag here.
NODARO_UPDATE_CHECKonNooff stops the daily update check: no request leaves the install. For air-gapped installs.
NODARO_UPDATE_CHECK_TOKENempty (anonymous)NoA GitHub token for the update check, with no scopes.

See Updating.

MCP

VariableDefault.envWhat it does
MCP_ENABLEDoffNotrue or 1 serves the MCP endpoint.
MCP_PUBLIC_URLempty (the Nodaro Cloud MCP host)NoThe public address of your MCP host.
MCP_DYNAMIC_REGISTRATIONallowlistNoDynamic client registration for MCP clients: allowlist, open or off.
MCP_DCR_ALLOWLIST14 known clientsNoThe client names that may register in allowlist mode.

See MCP.

Sign-in and roles

VariableDefault.envWhat it does
EXTERNAL_SSO_PROVIDERSempty (SSO off)NoTrusted identity providers, as inline JSON or @/path/to/file.json. A malformed value stops the boot.
EXTERNAL_SSO_LINK_EXISTINGfalseNoWhether single sign-on may link to an existing local account.
PLATFORM_OWNER_EMAILemptyNoBusiness: the owner account, whose role no other admin can change.

See Single sign-on and First user and admin.

Integrations

VariableDefault.envWhat it does
FIGMA_PLUGIN_OAUTH_CLIENT_IDempty (plugin connection off)NoThe client ID of the developer app that the Figma plugin connects through.
Social network variablesemptyNoSee Social network apps.

For the Figma plugin, register a developer app on your install with <PUBLIC_URL>/v1/oauth/plugin/callback as a redirect URI and the scopes jobs:read, assets:read, assets:write and credits:read. Then set its client ID here. When the variable is empty, the plugin connection routes answer 503 and nothing else changes. See OAuth apps.

Build-time settings

These are fixed when the image is built. Change them in the compose file's build: args: and rebuild.

Build argumentDefaultWhat it does
VITE_EDITIONcommunity in the compose fileThe edition the editor shows. The build refuses an empty or unknown value.
VITE_STUDIO_URLhttps://studio.nodaro.aiThe address for Open in Studio links.
VITE_PERSON_URLhttps://person.nodaro.aiThe address for the Open Person card on the home screen.

Video and audio editors

Edit in NodarCut on a video result on the canvas, and Edit on a video result in an app, open the NodarCut video editor. Edit on an audio result in an app opens the AudioMass editor. Your media does not go to the editor's server: the app reads the file from your own storage and hands it to the editor inside the browser. The editing and the export happen there.

  • Video. With FREECUT_URL empty, the editor loads from freecut.nodaro.ai, which allows embedding only from http://localhost:3000. On another origin, such as a LAN address or a domain, the browser refuses it and the panel explains why. Open an issue on GitHub with the URL you serve to have it allowed, or run your own FreeCut. FreeCut is public, MIT-licensed and ships a Dockerfile. Then set FREECUT_URL=https://freecut.example.internal.
  • Audio. There is no public hosted AudioMass, so until you configure it, Edit on an audio result opens no editor. On the Present tab it does nothing, and outside the editor it shows a panel that explains what to set. Run Nodaro's fork, AudioMass, which supports the in-browser handoff that the original AudioMass lacks. Then set AUDIOMASS_URL=https://audiomass.example.internal.

Restart the stack after a change. No rebuild is needed.

Model maker logos

GET /v1/models names the maker of each model and can carry the maker's logo as logoUrl, for apps that draw a model picker. A company's logo is its trademark, so no logo ships with Nodaro. An install publishes only the logos it has the right to show. Every other maker's logoUrl is null, and apps then show the maker's name. An install that publishes no logo needs no setup.

To publish a maker's logo:

  1. Upload the file to the bucket behind R2_PUBLIC_URL, at brand/makers/<maker id>.svg, with the content type image/svg+xml. Use the company's official logo as an SVG without scripts. The maker ids are the maker.id values that GET /v1/models returns.
  2. Declare the maker ids in the instance setting model_maker_logos, a list such as ["google", "openai"]. On a Business install, the platform operator sets it with PUT /v1/admin/settings/model_maker_logos and the body {"value": ["google", "openai"]}. On the Community Edition, which has no admin panel, write the same instance setting directly in the database.
  • The server checks the file first. A declared maker gets a logoUrl only once its file is in the bucket as an SVG, so the two steps can come in either order. A missing or wrongly typed file gives null, never a broken address.
  • Never open the address in a browser to test an upload. The CDN, and Caddy on the bundled stack, keep a "not found" answer for a year. Check the file with the storage console or a HEAD request, such as curl -I.
  • Timing. A new logo appears within about ten minutes. To replace a logo, upload the new file over the old one: the address changes with the file. To withdraw one, remove its id from the setting or delete the file.
  • Nodaro never deletes these files. Remove a logo from the storage console when you mean to.

Default language

DEFAULT_LOCALE sets the language that a new visitor starts in. The values are he, ar, de, fr, es, hi, ja, ko, pt-BR, ru, zh-CN and en. A user's own saved choice always wins. An empty, blank or unknown value falls back to the visitor's browser language. Restart to apply. See Languages.

Tutorial packs

A self-hosted install can add its own tutorials to the built-in set, without a rebuild. Each pack is a directory:

  • manifest.json — the pack's name and the categories its tutorials use, each with a slug, a name, and an optional sortOrder and description. Optional keys: locale, version, forbiddenPromptTerms, and creatorDisplayName, the author name shown on every card of the pack.
  • One *.json file per tutorial — a workflow snapshot with its nodes, its connections and its results already made, so a clone opens showing the finished run. Optional card data: estimatedCredits, the credits a run costs, and estimatedPerMinuteCredits for a tutorial whose price follows the length of the recording it is given. Such a tutorial sets estimatedCredits to the fixed part and estimatedPerMinuteCredits to the credits per minute of the recording. Also optional: nodeTypesUsed and providersUsed for the chips on the card, a per-tutorial creatorDisplayName, and listedIn. listedIn is ["tutorial"] for the Tutorials tab, the default, or ["marketplace"] for the template marketplace. It applies only on the first seed.

Every media file a tutorial uses must be a public https:// URL. Each tutorial's category must be declared in the manifest, and it is created on the first boot.

Mount the pack directories into the container read-only, and list their paths inside the container:

  nodaro:
    volumes:
      - ./tutorials/acme:/data/tutorials/acme:ro
NODARO_TUTORIAL_PACKS=/data/tutorials/acme

Packs only add tutorials; they never change the built-in ones. A pack that fails validation is skipped whole and logged. Validation fails on invalid JSON, an undeclared category, a media URL that is not public, or a slug that collides with an existing tutorial. Restart the container to load a changed pack.

Social network apps

Publishing to a social network works per network. A network becomes available once its OAuth app credentials are set. Without them, the network still appears in Settings › Integrations as unavailable, with the names of the missing variables.

NetworkVariables
InstagramMETA_APP_ID, META_APP_SECRET. Optional: META_INSTAGRAM_CONFIG_ID for Facebook Login for Business.
Instagram, without a Facebook PageINSTAGRAM_APP_ID, INSTAGRAM_APP_SECRET. Meta issues these apart from the Facebook app. Tokens refresh by themselves, about every 60 days.
FacebookMETA_APP_ID, META_APP_SECRET. Optional: META_FACEBOOK_CONFIG_ID.
TikTokTIKTOK_CLIENT_KEY, TIKTOK_CLIENT_SECRET
YouTubeGOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET
LinkedInLINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET
XX_CLIENT_ID, X_CLIENT_SECRET
RedditREDDIT_CLIENT_ID, REDDIT_CLIENT_SECRET
PinterestPINTEREST_CLIENT_ID, PINTEREST_CLIENT_SECRET
DiscordDISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, DISCORD_BOT_TOKEN
TwitchTWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET
ThreadsTHREADS_APP_ID, THREADS_APP_SECRET
MastodonMASTODON_CLIENT_ID, MASTODON_CLIENT_SECRET. Optional: MASTODON_URL, default mastodon.social.
TelegramNone: users paste their own bot token.
BlueskyNone: users connect with a handle and an app password.
Dev.to, Hashnode, MediumNone: users connect with their own API key or token.
WordPressNone: users connect with the site URL and an application password.
LemmyNone: users connect with an instance, a login and a community.

Every OAuth app must allow the redirect URI https://<your-domain>/v1/social/callback/<network>. When a Facebook or Instagram login manages more than one Page or account, the connect window asks the user which one to connect. See Publishing to social networks.

Frequently asked questions

Why does a variable in my .env file have no effect?

The compose file passes only the variables listed under the nodaro service's environment block to the app. Add any other variable to that block in docker-compose.community.yml, then run docker compose up -d.

Which environment variables are required?

None on the bundled compose stack, which sets or generates them. Outside it, the API refuses to start without SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY and an INTERNAL_ORCHESTRATOR_SECRET of at least 32 characters.

Do I need to restart after I change a variable?

Yes. Nodaro reads its environment at boot, so run docker compose up -d to recreate the container with the new values. Provider keys pasted on /setup are the exception and apply without a restart.

How do I change the default language of my install?

Set DEFAULT_LOCALE, for example DEFAULT_LOCALE=de, and restart. A new visitor starts in that language. A user's own saved choice always wins, and an unknown value falls back to the browser's language.

How do I add my own tutorials to a self-hosted install?

Put each tutorial pack in a directory with a manifest.json and one JSON file per tutorial, mount it into the container, and list the directories in NODARO_TUTORIAL_PACKS. Restart to load them.

Last updated on

On this page