Port Configuration Information
PlantPulse operates as a Docker Compose stack, and each container has its own ports. The same port design is followed for both single servers and clusters.
The ports bound to the host are determined by ports: of compose/docker-compose.yml. The firewall list opened by install.sh is broader than that. The fact that it is in the firewall does not mean a response will come through that port.
The first table on this page contains all ports that are actually open on the host. Other ports only communicate within the containers.
Which Container Opens What
| Container | Ports opened on host |
|---|---|
plantpulse-proxy | 80 · 443 · 1883 · 1884 — The sole entry point for users and equipment |
plantpulse-plugin-opcua | 11004 · 11005 |
plantpulse-ha | 10210 |
plantpulse-datalake | All infrastructure series (Table below) |
plantpulse-batch-web | 9500 · 9501 (PP_BATCH_HTTP_PORT · PP_BATCH_TLS_PORT) |
plantpulse-warehouse-web | 9600 · 9601 (PP_WAREHOUSE_HTTP_PORT · PP_WAREHOUSE_TLS_PORT) |
plantpulse-server-web · plantpulse-plugin-aasx | None — Operates only behind the proxy |
plantpulse-certs | None (Does not open ports) |
Port Topology
Recommended External Exposure (User / External System)
| Port | Container | Protocol | Purpose | Recommendation |
|---|---|---|---|---|
| 80 | proxy | HTTP | Operation Console / REST API | Reverse Proxy Recommended |
| 443 | proxy | HTTPS | Operation Console / REST API (TLS) | ✓ |
| 4950 | datalake | HTTPS | Monitor UI + /api/health | Monitoring System |
| 1883 | proxy | TCP | MQTT Plaintext — Edge Device / Sensor collection | Private Network Recommended |
| 1884 | proxy | TCP+TLS | MQTT over TLS (Proxy is passthrough, Broker terminates) | TLS Recommended |
| 11004 / 11005 | opcua-server | OPC-UA | Endpoint for external equipment | TLS Recommended |
| 10210 | ha | HTTP | HA Daemon /api/health · /api/cluster — Peers connect in 2-cluster config | Peer IP only |
| 9094 | datalake | TCP+TLS | Kafka External Client | TLS Required |
| 5500 / 5501 | datalake | HTTP / HTTPS | External system data inquiry | API Key |
Operational Advice: It is recommended to expose only 80/443 externally and keep the rest within a private network.
The batch and warehouse containers bind these four to the host (first table above). This is intended to allow operators to query those containers directly, not to expose them externally. Block them at the upper firewall and perform checks within the host.
curl -fsS http://127.0.0.1:9500/api/health | jq # 배치
curl -fsS http://127.0.0.1:9600/api/health | jq # 웨어하우스
To change ports, modify PP_BATCH_HTTP_PORT · PP_BATCH_TLS_PORT · PP_WAREHOUSE_HTTP_PORT · PP_WAREHOUSE_TLS_PORT of .env.
Real-time push is sent through the proxy (443). No separate WebSocket port is opened on the host.
Messaging
| Port | Container | Protocol | Purpose |
|---|---|---|---|
| 1883 / 1884 | proxy | TCP / TLS | MQTT (HiveMQ). Published by proxy |
| 18883 / 18884 | datalake | TCP / TLS | Reserved. No current listener opening these ports |
| 9092 | datalake | TCP | Kafka PLAINTEXT (Private network only) |
| 9093 | datalake | TCP | Kafka KRaft Controller |
| 9094 | datalake | TCP+TLS | Kafka SASL_SSL |
After bootstrapping, Kafka clients reconnect to the address provided by advertised.listeners. Placing a proxy in front would require changing that address as well; otherwise, only the bootstrap succeeds, followed by a silent failure. Therefore, Kafka ports are published directly by the datalake.
As browser real-time push switched to SSE, the STOMP broker was removed from the datalake image. 61000 / 61004 are no longer serviced. Please clean up any remaining old firewall rules.
Storage (Private network only)
| Port | Protocol | Purpose |
|---|---|---|
| 9042 | CQL | Aster TSDB |
| 7000 / 7001 | TCP | Aster TSDB Gossip / SSL |
| 5432 | TCP | PostgreSQL |
| 6379 | TCP | Valkey (Redis compatible) |
| 6380 | TCP+TLS | Valkey TLS listener |
| 9000 | HTTP | RustFS API |
| 9001 | HTTP | RustFS Console |
Analytics / SQL (Private network only)
| Port | Protocol | Purpose |
|---|---|---|
| 7077 | TCP | Spark Master RPC |
| 4440 | HTTP | Spark Master UI — Not 8080 |
| 8081 | HTTP | Spark Worker UI |
| 10000 | Thrift | Kyuubi SQL Gateway |
| 10099 | HTTP | Kyuubi REST API |
| 19001 | HTTP | Gravitino Integrated Catalog |
| 9083 | Thrift | Hive Metastore |
| 3500 / 3600 | HTTP | Lab Analysis Environment |
| 4000 / 4001 | HTTP / HTTPS | SQL Query Service |
Processing / Time Series (Private network only)
| Port | Protocol | Purpose |
|---|---|---|
| 7400 / 7401 | HTTP / HTTPS | CEP (Aster CEP) |
| 7800 / 7801 | HTTP / HTTPS | Time Series Engine (TSE) |
| 3000 | HTTP | PlantPulse Dashboard |
| 5500 / 5501 | HTTP / HTTPS | Data Gateway |
Workflow (Private network only)
| Port | Protocol | Purpose |
|---|---|---|
| 7233 | gRPC | Temporal Frontend |
| 8233 | HTTP | Temporal Web UI |
| 8380 | HTTP | Kestra (UI + API) |
| 8381 | HTTP | Kestra management port |
Other Datalake Ports
| Port | Purpose |
|---|---|
| 7003 | Diagnostic Console (Private network only) |
| 60000 / 60001 | OPC Agent collection (Private network only) |
| 6199 / 6299 / 6399 / 6499 / 7099 | JMX agent — Please allow Control Node IP only |
These four ports belong to the app tier — 9500 · 9501 are two Tomcat connectors for plantpulse-batch-web, and 9600 · 9601 are for plantpulse-warehouse-web. Both are HTTP and HTTPS connectors, and 9501 · 9601 are full connectors with their own SSLHostConfig, not just for redirection.
Definitions from when batch/warehouse were in the same container as the datalake remained on the datalake side, and at that time, they held host ports with nothing behind them, causing connection resets (discovered 2026-09-02). Those definitions have been cleaned up.
And on 2026-09-07, app containers began opening them themselves (User decision). Thus, the statement «App containers do not publish anything» is no longer true. It is therefore more important to remove these ports from the datalake list — if both try to grab the same port, the container that starts later will fail to boot with an error stating «only the port name».
Container Internal Only (Not disclosed to host)
The following are ports that communicate only inside the container. There is no response when querying curl from the host.
| Port | Target | Alternative Verification Method |
|---|---|---|
| 8090 · 4800 / 4801 | AAS(BaSyx) V3 API · V2 Registry/AAS | Consider a separate reverse proxy if external access is required |
| 7199 · 7279 | Aster TSDB · HiveMQ JMX | Enter datalake via bin/shell.sh then verify |
App ports of plantpulse-server-web | Inside that container | bin/status.sh / bin/logs.sh <컨테이너> |
The console and health API are serviced on both ports — 4950(HTTPS) and 4949(plaintext HTTP). They are the same console and same API, differing only in scheme. 4949 no longer redirects to 4950.
4949 is plaintext — login passwords and session cookies flow in the clear. Use 4950 on untrusted networks. 4949 is an option for environments where self-signed certificate warnings actually block the operator.
# 호스트 / 외부에서
curl -kfsS https://<server-ip>:4950/api/health | jq
# 컨테이너 안에서 (어떤 구성에서도 동작합니다)
docker exec plantpulse-datalake curl -kfsS https://127.0.0.1:4950/api/health | jq
Pre-installation Port Conflict Check
# 핵심 포트 점유 확인
ss -tlnp | grep -E ':(80|443|1883|1884|3000|4000|4950|5432|5500|6379|7077|7233|7400|7800|8233|8380|9000|9042|9092|10000|10210|11004|19001)\s'
If there is no output, there is no conflict. If there is output:
# 점유 프로세스 확인
sudo lsof -i :<port>
sudo ss -tlnp | grep ":<port>"
Commonly conflicting ports: 80 (Apache/Nginx), 5432 (External PostgreSQL), 3000 (Grafana/Node.js), 9092 (External Kafka), 6379 (External Redis). If using external services, it is simpler to move the port of that service.
bin/preflight.sh non-destructively checks major ports (80 · 443 · 4949 · 4950) before installation.
9500 · 9501 · 9600 · 9601 are missing from the single line of
ssabove. Since batch/warehouse use those ports, please include them in the regular expression to see if those four are also empty.
Firewall Configuration Examples
install.sh automatically configures the host firewall (firewalld / ufw). Below are examples for when you must manually allow ports in the upper firewall (corporate network, cloud security group, router).
firewalld (RHEL/Rocky/Oracle)
# 외부 노출 (모든 IP)
for port in 80 443 4950; do
firewall-cmd --permanent --add-port=${port}/tcp
done
# 외부 노출 (TLS 메시징 / 설비)
for port in 1884 9094 11004 11005; do
firewall-cmd --permanent --add-port=${port}/tcp
done
# 사설망 only (RFC 1918 + Tailscale CGN 허용)
for src in 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10; do
for port in 1883 5432 6379 7077 7233 7800 8233 8380 9000 9001 9042 9092 10000 10210 19001; do
firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=${src} port port=${port} protocol=tcp accept"
done
done
firewall-cmd --reload
ufw (Ubuntu)
# 외부 노출
for port in 80 443 4950 1884 9094 11004 11005; do
ufw allow ${port}/tcp
done
# 사설망 only
for port in 1883 5432 6379 7077 7233 7800 8233 8380 9000 9042 9092 10000 10210 19001; do
for src in 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10; do
ufw allow from $src to any port $port
done
done
ufw enable
Verify Automatic Configuration
# RHEL/Rocky/Oracle
sudo firewall-cmd --list-ports
sudo firewall-cmd --list-rich-rules
# Ubuntu
sudo ufw status numbered
Cloud Security Groups (e.g., AWS / GCP / Azure)
In addition to the host firewall, cloud security groups must be configured identically.
| Inbound Rule | Source | Port |
|---|---|---|
| Operation Console | 0.0.0.0/0 (or Corp IP) | 80, 443 |
| Health Check | Monitoring Node | 4950 |
| MQTT | Device | 1883, 1884 |
| OPC-UA | Equipment | 11004, 11005 |
| Kafka TLS | External Client | 9094 |
| Private Network | VPC CIDR | 1883, 5432, 6379, 7077, 9042, 9092, 10000, 10210, … |
Related Documents
- System Requirements — Hardware / OS / Tuning
- One-line Install (Recommended) — Automatic firewall configuration
- Docker Installation — Container configuration and port mapping
- Module Index — Detailed ports by module
- Administrator: Port and Service Management — Operational inspection procedures