Skip to content

PHP, Java, Node and Python on one Windows server: the coexistence model

Verified 2 min read

Design one Windows host with IIS/Nginx on 443, isolated runtimes on internal ports, one supervisor per app, and a port/identity map you can audit.

Tested onStandard / Datacenter · Build 26100.33650 · x64
E1
Build
26100.33650
Edition
Standard / Datacenter
Architecture
x64
Support
Documentation (E1)
Evidence
E1Official documentation
Verified
2026-10-29
Page type
Concept

One public edge, many private runtimes:

443 IIS/Nginx (TLS, routing, logs)
├─ app-a.local -> 127.0.0.1:3000 Node
├─ app-b.local -> 127.0.0.1:8080 Tomcat
├─ app-c.local -> 127.0.0.1:8000 Python/Waitress
└─ php.local -> FastCGI PHP

Only the edge listens on 80/443. Everything else binds 127.0.0.1 (or a private IP) and is reached through the proxy or FastCGI.

App Runtime Internal endpoint Supervisor Identity Logs
app-a Node 20 127.0.0.1:3000 WinSW svc-app-a C:\logs\app-a
app-b Tomcat/JDK 17 127.0.0.1:8080 Tomcat service svc-app-b C:\tomcat...\logs
app-c Python 3.11 127.0.0.1:8000 NSSM svc-app-c C:\logs\app-c
php PHP 8.4 FastCGI pipe/port IIS FastCGI app pool C:\inetpub\logs

If two rows share a port, one app will fail to start or the proxy will route to the wrong process.

  • One app, one service identity. Do not run all apps as LocalSystem.
  • One app, one log directory. Shared logs make incidents unreadable.
  • One app, one dependency tree. Node node_modules, Python venv, Java WEB-INF/lib; do not share global packages.
  • One version per app. Pin absolute paths; no global PATH switch.
  • One supervisor per app. Do not let IIS HPH and a Windows service both start the same process.
  • IIS + ARR if you already run IIS, need Windows auth, or want IIS logging.
  • Nginx if you want simple server blocks and a small config surface.
  • Do not run both on 443. Pick one edge and make the other internal-only.

TLS terminates at the edge. Backends speak HTTP on localhost; do not expose their ports through the firewall. For PHP, FastCGI is not an HTTP port; it is a pipe/port between IIS/Nginx and php-cgi.

  1. Edge (IIS/Nginx) can start first; it returns 502 until backends are up.
  2. Backend services start automatically.
  3. Health checks distinguish “edge up, app down” from “everything down”.

A 502 is useful: it says the edge is alive and the upstream is not. A connection refused on 443 says the edge is down.

Terminal window
Get-NetTCPConnection -State Listen |
Where-Object LocalPort -in 80,443,3000,8000,8080 |
Select-Object LocalAddress,LocalPort,OwningProcess
Get-CimInstance Win32_Process -Filter "Name='node.exe' OR Name='java.exe' OR Name='python.exe'" |
Select-Object ProcessId,Name,ExecutablePath,CommandLine

Cross-check PID → app → port → hostname. If a port is owned by the wrong PID, stop guessing and fix the service definition.

E1. The lab verified IIS/PHP and Nginx separately; it did not run Node/Java/Python behind one edge. The map is the design to validate on Server 2025.

Sources