Skip to content

Environment Variables Explained

IoT DC3 has two separate sets of environment files, and they target different consumers. The root .env feeds Docker Compose interpolation; dc3/env/dev.env(.sh) feeds local Java processes. By the end of this page you'll know which variable belongs in which file, why localhost ports differ from the ports a container sees, and which two secrets you must change before going to production.

You are here: you've already developed locally from source or brought the stack up with Compose. Next, see Deployment Modes and Image Sources to learn how the whole stack starts.

Why Two Sets of Files

The same variable name (say POSTGRES_HOST) means different things inside a container versus in a Java process on your laptop. Containers reach each other by service name on the Compose network (dc3-postgres). A Java process running in your IDE lives on the host, so it can only connect through the ports Compose publishes to the host (such as localhost:35432). The two file sets exist for these two non-overlapping paths. Mix them up and a local process tries to resolve a container name it can't reach, or a container tries to talk to a localhost it can't see.

FileConsumerPurposeInjection Method
.env.exampleDocker Compose templateCopied to root .env; defines image registry, image tag, published portsCompose variable interpolation
.envDocker ComposeUntracked local config, interpolated by dc3/docker-compose*.ymlCompose variable interpolation
dc3/env/dev.envIDE (EnvFile plugin)Environment variables for local Java processes, without exportRead by the IDE EnvFile plugin
dc3/env/dev.env.shShellEnvironment variables for local Java processes, with export, loaded via sourceInjected into the shell environment

The root .env is not injected into local Java processes

The root .env serves Docker Compose only. It is not automatically passed to Java processes started by your IDE or command line. Running from source locally requires dc3/env/dev.env(.sh), which points the process at the dependency ports Compose publishes on localhost. Setting POSTGRES_HOST=localhost in the root .env changes nothing about any container's runtime environment.

Two Isolated Paths

The diagram shows how each file set takes effect along its own path. The key point: these two paths never cross. Compose does not read dev.env.sh, and local Java processes do not read the root .env.

The mapping between host ports and internal ports is the core of this diagram:

Dependencyhost (for local processes)internal (for container-to-container)
PostgreSQLlocalhost:35432dc3-postgres:5432
RabbitMQ AMQPlocalhost:35672dc3-rabbitmq:5672
EMQX MQTTlocalhost:31883dc3-emqx:1883 (conventional value)

How to Use

Bring the Stack Up with Compose

Create a local .env from the template, then bring it up with make (backed by podman compose):

bash
cp .env.example .env
make up-db && make up-optional && make up-dev
bash
cp .env.example .env
podman compose -f dc3/docker-compose-dev.yml config --quiet

Variables in the root .env interpolate the compose files, for example images and published ports:

yaml
image: ${DC3_IMAGE_REGISTRY:-pnoker}/dc3-gateway:${DC3_IMAGE_TAG:-2026.6}
ports:
  - "${DC3_BIND_HOST:-127.0.0.1}:${DC3_GATEWAY_PORT:-8000}:8000"

Compose does not inject every variable in .env into every container. Only the variables the compose files explicitly reference via environment or env_file reach a container.

Run Locally from Source

source the file before starting a Java process from the command line:

bash
source dc3/env/dev.env.sh

Without it, the local process falls back to container service names (dc3-postgres, dc3-rabbitmq, dc3-center-manager) or default ports, so it can't reach the dependencies on your machine. JetBrains IDEA users should use dc3/env/dev.env instead (same content, no export): install the EnvFile plugin, enable EnvFile in the Run Configuration, and add dc3/env/dev.env. Don't use .env.example directly as an IDEA environment file — it's a Compose template, not Java runtime configuration.

Key Variables Grouped by Scenario

The groups below cover what you actually need for a given scenario. The full long table is collapsed under ::: details at the end of the page; here we list only the variables you'll really change or look up when troubleshooting. The three Scope values mean: Runtime (read by both local and in-container Java processes), Compose only (only the root .env for Compose), and Per-process (a single-service override).

Security Keys (Runtime)

Two secrets are the platform's root of identity. Their defaults exist only for first-run convenience and must never go to production.

VariableDefaultPurpose
DC3_SECURITY_KEYdc3.security.key.2026.io.github.pnokerSigning key the auth center uses to generate and verify login tokens
AUTH_HMAC_SECRETio.github.pnoker.dc3HMAC-SHA256 key the gateway uses to sign X-Auth-Principal for backend services

Change to strong random values in production, and never commit or print them

DC3_SECURITY_KEY and AUTH_HMAC_SECRET ship with defaults. In production, change them to strong random values and never commit them to the repository, write them to logs, or print them as-is. When the Spring profile is pre or pro and AUTH_HMAC_SECRET is empty or still equals the default io.github.pnoker.dc3, the service throws IllegalStateException and refuses to start. This is an intentional security gate — don't bypass it.

PostgreSQL (Runtime)

Local processes connect to localhost:35432; in-container processes connect to dc3-postgres:5432. POSTGRES_SCHEMA overrides the schema only inside a single-service process (such as dc3_manager or dc3_data) — don't set it globally.

VariableDefaultScopePurpose
POSTGRES_HOSTlocalhostRuntimelocalhost locally, dc3-postgres in-container
POSTGRES_PORT35432Runtimehost-published port; internal is 5432
POSTGRES_USERNAMEdc3RuntimeUsername
POSTGRES_PASSWORDdc3dc3dc3RuntimePassword
POSTGRES_DBdc3RuntimeDatabase name
POSTGRES_SCHEMA(unset)Per-processSingle-service schema override
DC3_POSTGRES_PORT35432Compose onlyPort the container publishes to the host

RabbitMQ (Runtime)

AMQP host port 35672, internal 5672. With TLS on, the internal port switches to 5671.

VariableDefaultScopePurpose
RABBITMQ_HOSTlocalhostRuntimelocalhost locally, dc3-rabbitmq in-container
RABBITMQ_PORT35672RuntimeAMQP host port; internal 5672
RABBITMQ_USERNAMEdc3RuntimeUsername
RABBITMQ_PASSWORDdc3dc3dc3RuntimePassword
RABBITMQ_VIRTUAL_HOSTdc3Runtimevirtual host
RABBITMQ_SSL_ENABLEDfalseRuntimeEnable TLS (uses 5671 when true)
DC3_RABBITMQ_PORT35672Compose onlyAMQP published port
DC3_RABBITMQ_MANAGEMENT_PORT15672Compose onlyManagement UI published port

EMQX / MQTT (Runtime)

MQTT broker host port 31883. EMQX also publishes several other ports (WebSocket, Dashboard) — see the collapsed long table for the full list.

VariableDefaultScopePurpose
MQTT_BROKER_HOSTlocalhostRuntimebroker host
MQTT_BROKER_PORT31883Runtimebroker port (published by EMQX; internal around 1883)
MQTT_USERNAMEdc3RuntimeUsername
MQTT_PASSWORDdc3dc3dc3RuntimePassword
DC3_EMQX_MQTT_PORT31883Compose onlyMQTT published port
DC3_EMQX_DASHBOARD_PORT18083Compose onlyDashboard published port

gRPC / facade (Runtime)

Center services talk to each other through facades. Distributed deployments default to DC3_FACADE_MODE=grpc, and local processes point CENTER_*_HOST at localhost.

VariableDefaultScopePurpose
CENTER_AUTH_HOSTlocalhostRuntimeAuth center host
CENTER_MANAGER_HOSTlocalhostRuntimeManager center host
CENTER_DATA_HOSTlocalhostRuntimeData center host
CENTER_AGENTIC_HOSTlocalhostRuntimeAgentic center host
DC3_FACADE_MODEgrpcRuntimefacade protocol mode
DC3_FACADE_GRPC_DEADLINE_MS3000RuntimePer-request gRPC deadline; 0 disables the client deadline

Gateway and Service Ports (Compose only)

The gateway is the only external HTTP entry point (8000). The HTTP and gRPC published ports of each center are listed below. SERVER_PORT and GRPC_SERVER_PORT are single-process overrides, useful only when you run multiple services locally and need to avoid port collisions.

VariableDefaultScopePurpose
DC3_GATEWAY_PORT8000Compose onlyGateway HTTP published port (entry point)
DC3_AUTH_PORT8300Compose onlyAuth center HTTP
DC3_MANAGER_PORT8400Compose onlyManager center HTTP
DC3_DATA_PORT8500Compose onlyData center HTTP
DC3_AGENTIC_PORT8600Compose onlyAgentic center HTTP
DC3_AUTH_GRPC_PORT9300Compose onlyAuth center gRPC
DC3_MANAGER_GRPC_PORT9400Compose onlyManager center gRPC
DC3_DATA_GRPC_PORT9500Compose onlyData center gRPC
DC3_LISTENING_VIRTUAL_TCP_PORT6270Compose onlyListening Virtual driver TCP publish
DC3_LISTENING_VIRTUAL_UDP_PORT6271Compose onlyListening Virtual driver UDP publish
SERVER_PORT(unset)Per-processSingle-service HTTP port override
GRPC_SERVER_PORT(unset)Per-processSingle-center gRPC port override

DC3_LISTENING_VIRTUAL_*_PORT are host-published ports

DC3_LISTENING_VIRTUAL_TCP_PORT and DC3_LISTENING_VIRTUAL_UDP_PORT are the ports Compose publishes to the host. The process's internal ports use TCP_PORT and UDP_PORT (Per-process). Don't confuse the two.

Agentic / AI (Runtime)

The AGENTIC_FALLBACK_OPENAI_* group only kicks in as a fallback when dc3_model_provider has no usable provider configured. Conversation memory is off by default.

VariableDefaultPurpose
AGENTIC_FALLBACK_OPENAI_BASE_URLhttps://api.openai.comFallback OpenAI-compatible API address
AGENTIC_FALLBACK_OPENAI_API_KEY(empty)Fallback API key (fill in when the endpoint requires authentication)
AGENTIC_FALLBACK_OPENAI_MODELgpt-4oFallback model name
AGENTIC_FALLBACK_OPENAI_TEMPERATURE0.7Sampling temperature (0.0–2.0)
AGENTIC_FALLBACK_OPENAI_MAX_TOKENS2048Maximum output tokens
AGENTIC_MEMORY_SCHEMA_INITneverSpring AI JDBC memory table init mode (always/never/create_if_not_exists)
AGENTIC_MEMORY_ENABLEDfalseWhether persistent conversation memory is on
AGENTIC_TOOL_CALLING_ENABLEDtrueWhether tool calling is on
AGENTIC_MEMORY_MAX_MESSAGES50Maximum messages retained per conversation window
AGENTIC_ATTACHMENT_STORAGE_PATHdc3/data/agentic/attachmentsAttachment storage path

The table binding for AGENTIC_MEMORY_SCHEMA_INIT is governed by the code

AGENTIC_MEMORY_SCHEMA_INIT defaults to never and is meant to control the memory table init mode. But after Compose injects it into the container, no application*.yml in the repository binds it to Spring AI's initialize-schema; the memory tables are actually pre-created by the initdb scripts. Whether setting it to always truly triggers automatic table creation needs to be determined from the code — don't treat it as a wired, working switch.

Real API keys must not enter docs, logs, or commit history

Sensitive values like AGENTIC_FALLBACK_OPENAI_API_KEY must never be written into docs, logs, or commit history. Configure production providers in the dc3_model_provider table; the fallback is just a safety net.

Defaults in the table come from .env.example and differ from the in-code fallback defaults

The values above for AGENTIC_MEMORY_ENABLED (false) and AGENTIC_ATTACHMENT_STORAGE_PATH (dc3/data/agentic/attachments) come from .env.example. On the cp .env.example .env + source path, these values are explicitly injected. But the in-code fallback defaults in application-agentic.yml differ: when the environment variables are unset, memory defaults to enabled and the attachment path is dc3/data/upload/agentic/attachment. When you're not on the .env.example path, the code takes precedence.

Batch Processing (Runtime)

MQTT and point values each have a "count threshold + interval" pair. A Quartz schedule flushes the accumulated buffer all at once every interval seconds, where speed = count / interval.

VariableDefaultPurpose
MQTT_BATCH_SPEED100MQTT batch size threshold (records/batch)
MQTT_BATCH_INTERVAL5MQTT batch interval (seconds)
POINT_BATCH_SPEED100Point value batch size threshold
POINT_BATCH_INTERVAL5Point value batch interval (seconds)

Image Sources (Compose only)

On mainland China networks, set REGISTRY to cn to use the Aliyun mirror. Note: the Makefile reads REGISTRY and Compose interpolation reads DC3_IMAGE_REGISTRY — each governs its own segment.

VariableDefaultPurpose
REGISTRYautoMakefile image-source selector (accepts only auto/global/cn; other values error out)
DC3_IMAGE_REGISTRYpnokerImage namespace
DC3_IMAGE_TAG2026.6Image tag for all services and dependencies
DC3_BIND_HOST127.0.0.1Published-port bind address (0.0.0.0 for external access)

Observability (Compose only / Runtime)

The optional stack (EMQX, ELK, Prometheus, Grafana) comes up via make up-optional. Its ports and JVM parameters are below.

VariableDefaultScopePurpose
GF_SERVER_ROOT_URLhttp://localhost:3000RuntimeGrafana external root URL
DC3_GRAFANA_PORT3000Compose onlyGrafana published port
DC3_KIBANA_PORT5601Compose onlyKibana published port
DC3_ES_JAVA_OPTS-Xms512m -Xmx512mRuntimeElasticsearch JVM heap
DC3_LS_JAVA_OPTS-Xms256m -Xmx256mRuntimeLogstash JVM heap
APM_AGENT_ENABLEfalseRuntimeWhether the Java APM agent is on
Full Variable Reference (collapsed)

Security & Authentication (Runtime)

VariableDefaultPurpose
DC3_SECURITY_KEYdc3.security.key.2026.io.github.pnokerLogin token signing key
AUTH_HMAC_SECRETio.github.pnoker.dc3X-Auth-Principal HMAC-SHA256 key

PostgreSQL

VariableDefaultScope
POSTGRES_HOSTlocalhostRuntime
POSTGRES_PORT35432Runtime
POSTGRES_USERNAMEdc3Runtime
POSTGRES_PASSWORDdc3dc3dc3Runtime
POSTGRES_DBdc3Runtime
POSTGRES_SCHEMA(unset)Per-process
DC3_POSTGRES_PORT35432Compose only

RabbitMQ

VariableDefaultScope
RABBITMQ_HOSTlocalhostRuntime
RABBITMQ_PORT35672Runtime
RABBITMQ_USERNAMEdc3Runtime
RABBITMQ_PASSWORDdc3dc3dc3Runtime
RABBITMQ_VIRTUAL_HOSTdc3Runtime
RABBITMQ_MQTT_EXCHANGEdc3.e.mqttRuntime
RABBITMQ_SSL_ENABLEDfalseRuntime
RABBITMQ_SSL_ALGORITHMTLSRuntime
RABBITMQ_SSL_VALIDATE_SERVER_CERTIFICATEfalseRuntime
RABBITMQ_SSL_VERIFY_HOSTNAMEfalseRuntime
RABBITMQ_CONTAINER_PORT5672Runtime
DC3_RABBITMQ_PORT35672Compose only
DC3_RABBITMQ_TLS_PORT35671Compose only
DC3_RABBITMQ_MANAGEMENT_PORT15672Compose only

EMQX / MQTT

VariableDefaultScope
MQTT_BROKER_HOSTlocalhostRuntime
MQTT_BROKER_PORT31883Runtime
MQTT_USERNAMEdc3Runtime
MQTT_PASSWORDdc3dc3dc3Runtime
MQTT_BATCH_SPEED100Runtime
MQTT_BATCH_INTERVAL5Runtime
DC3_EMQX_WS_PORT38083Compose only
DC3_EMQX_WSS_PORT38084Compose only
DC3_EMQX_MQTT_PORT31883Compose only
DC3_EMQX_MQTTS_PORT38883Compose only
DC3_EMQX_DASHBOARD_PORT18083Compose only

gRPC / facade

VariableDefaultScope
CENTER_AUTH_HOSTlocalhostRuntime
CENTER_MANAGER_HOSTlocalhostRuntime
CENTER_DATA_HOSTlocalhostRuntime
CENTER_AGENTIC_HOSTlocalhostRuntime
DC3_FACADE_MODEgrpcRuntime
DC3_FACADE_GRPC_DEADLINE_MS3000Runtime
DC3_AUTH_GRPC_PORT9300Compose only
DC3_MANAGER_GRPC_PORT9400Compose only
DC3_DATA_GRPC_PORT9500Compose only

HTTP Gateway & Service Ports

VariableDefaultScope
DC3_GATEWAY_PORT8000Compose only
DC3_AUTH_PORT8300Compose only
DC3_MANAGER_PORT8400Compose only
DC3_DATA_PORT8500Compose only
DC3_AGENTIC_PORT8600Compose only
SERVER_PORT(unset)Per-process
GRPC_SERVER_PORT(unset)Per-process
DC3_LISTENING_VIRTUAL_TCP_PORT6270Compose only
DC3_LISTENING_VIRTUAL_UDP_PORT6271Compose only
TCP_PORT(unset)Per-process
UDP_PORT(unset)Per-process
GATEWAY_ROUTE_AUTH_TOKEN_URI(unset)Per-process
GATEWAY_ROUTE_AUTH_URI(unset)Per-process
GATEWAY_ROUTE_MANAGER_URI(unset)Per-process
GATEWAY_ROUTE_DATA_URI(unset)Per-process
GATEWAY_ROUTE_AGENTIC_URI(unset)Per-process

Agentic / AI (Runtime)

VariableDefault
AGENTIC_FALLBACK_OPENAI_BASE_URLhttps://api.openai.com
AGENTIC_FALLBACK_OPENAI_API_KEY(empty)
AGENTIC_FALLBACK_OPENAI_MODELgpt-4o
AGENTIC_FALLBACK_OPENAI_TEMPERATURE0.7
AGENTIC_FALLBACK_OPENAI_MAX_TOKENS2048
AGENTIC_MEMORY_SCHEMA_INITnever
AGENTIC_MEMORY_ENABLEDfalse
AGENTIC_MEMORY_MAX_MESSAGES50
AGENTIC_TOOL_CALLING_ENABLEDtrue
AGENTIC_ATTACHMENT_STORAGE_PATHdc3/data/agentic/attachments

Batch Processing / Images / Observability

VariableDefaultScope
POINT_BATCH_SPEED100Runtime
POINT_BATCH_INTERVAL5Runtime
REGISTRYautoCompose only
DC3_IMAGE_REGISTRYpnokerCompose only
DC3_IMAGE_TAG2026.6Compose only
DC3_LOG_MAX_SIZE10MCompose only
DC3_LOG_MAX_FILE20Compose only
DC3_BIND_HOST127.0.0.1Compose only
GF_SERVER_ROOT_URLhttp://localhost:3000Runtime
DC3_GRAFANA_PORT3000Compose only
DC3_KIBANA_PORT5601Compose only
DC3_ES_JAVA_OPTS-Xms512m -Xmx512mRuntime
DC3_LS_JAVA_OPTS-Xms256m -Xmx256mRuntime
APM_AGENT_ENABLEfalseRuntime
NODE_ENVdevRuntime

Constraints and Common Pitfalls

  • Editing .env.example does nothing at runtime — you must cp .env.example .env first.
  • dc3/env/dev.env and the root .env serve different purposes. They are not the same file; don't copy one into the other.
  • Use DC3_*_PORT consistently for service published ports. The process internals still use Spring Boot's native names such as SERVER_PORT and GRPC_SERVER_PORT.
  • Per-process variables (POSTGRES_SCHEMA, SERVER_PORT, TCP_PORT, and so on) are single-service overrides only — don't set them globally.
  • The Compose application stack can use a different NODE_ENV than local source runs.

Further Reading

Released under the AGPL-3.0 License · 基于 AGPL-3.0 协议发布