Stack Reference
Plug these values into the environment pattern. Pick one backend row, one front-end row, and a serving model — then enter the corresponding values in each environment tab. The Examples show full combinations end to end.
Backends
The backend determines the runtime to install (via the pre-clone script, if it isn't already in the base image), the Post-clone Action that restores packages, and the Start Command.
| Backend | Runtime in base image? | Install dependencies | Start command | Default port |
|---|---|---|---|---|
| Node.js (Express) | Yes (Node.js 24) | npm ci | node index.js | 3000 |
| .NET (ASP.NET Core) | No — install the SDK | dotnet restore | dotnet run --urls http://0.0.0.0:5000 | 5000 |
| Java (Spring Boot) | No — install a JDK | mvn -q package -DskipTests | java -jar target/app.jar | 8080 |
| Python (FastAPI) | Yes (Python 3) | pip install --break-system-packages -r requirements.txt | uvicorn main:app --host 0.0.0.0 --port 8000 | 8000 |
| PHP (built-in server) | No — install PHP (Composer if used) | composer install (if used) | php -S 0.0.0.0:8000 router.php | 8000 |
The Default port column shows each stack's conventional port. The reference apps standardize every backend's API on port 3001 so the front-end proxy config is identical across the grid — so the two-process examples use 3001, not the default above. Use whatever port your app actually binds.
PHP apps are often server-rendered (for example Laravel with Blade templates) with no separate SPA. In that case skip the front-end matrix — PHP serves the HTML itself, which is a single-origin setup with no front-end build step. See the PHP example.
The base image's Python is PEP 668 "externally managed", so a plain pip install fails the build with error: externally-managed-environment. In the container, pass --break-system-packages (as shown above) — it's safe because the build container is disposable. When running on your own machine instead, use a virtualenv (python3 -m venv .venv && .venv/bin/pip install -r requirements.txt) rather than that flag.
For an edit-and-refresh dev loop, run the backend in watch mode so it reloads on code changes instead of needing a manual restart: dotnet watch run, node --watch index.js, uvicorn … --reload, or Spring Boot DevTools (then mvn -q -o compile triggers the restart; java -jar is for production). The reference apps run their backends this way, so the API hot-reloads just like the front-end dev server. A new API value still appears on the next browser refresh, since the page fetches /api/hello on load.
Installing a runtime (the pre-clone script)
You don't maintain a Dockerfile — CoderFlow generates it from your environment settings (preview it on the Build tab). Node.js and Python are already in the base image, so they need nothing here. For .NET, Java, and PHP, paste the runtime install into the pre-clone script on the Build tab (under Scripts → pre-clone) — raw Dockerfile instructions, injected before any repo is cloned:
.NET SDK
RUN curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh \
&& bash /tmp/dotnet-install.sh --channel 10.0 --install-dir /usr/local/dotnet \
&& ln -sf /usr/local/dotnet/dotnet /usr/local/bin/dotnet \
&& rm /tmp/dotnet-install.sh
ENV DOTNET_ROOT=/usr/local/dotnet
JDK 21 (Temurin) + Maven
RUN apt-get update \
&& apt-get install -y wget gnupg ca-certificates apt-transport-https \
&& mkdir -p /etc/apt/keyrings \
&& wget -qO - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor -o /etc/apt/keyrings/adoptium.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb $(. /etc/os-release && echo $VERSION_CODENAME) main" > /etc/apt/sources.list.d/adoptium.list \
&& apt-get update && apt-get install -y temurin-21-jdk maven \
&& rm -rf /var/lib/apt/lists/*
ENV JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64
This pins JDK 21 (LTS) regardless of the base image's default. For a different version, change temurin-21-jdk and JAVA_HOME together — or use the distro's default-jdk to take whatever it provides.
PHP + Composer
RUN apt-get update \
&& apt-get install -y php-cli php-mbstring php-xml php-curl php-zip composer unzip \
&& rm -rf /var/lib/apt/lists/*
Add the extensions your framework needs (for example php-pdo, php-mysql, php-sqlite3). For a specific PHP version, add the Sury APT repository before installing.
Proxying to a running server
Some environments don't start a server in the container at all — they proxy to an application server that's already running: set a Proxy URL on the Application Server instead of a Start Command. Use it when the app runs outside the container — a staging or remote dev server, or a hard-to-containerize app. A common case is an IBM i green-screen (5250) or Rich Display application served by Profound UI Genie, where an Interactive Sessions connection auto-configures the proxy for you — see Setting up an IBM i environment.
Front ends
The front end is nearly interchangeable from CoderFlow's point of view: in single-origin you build it to static files, and in two-process you run its dev server with a proxy. Only the commands differ — and with no framework there's nothing to build, since the files are served as-is.
| Front end | Create | Build → output (single-origin) | Dev server (two-process) | Proxy config | Dev port |
|---|---|---|---|---|---|
| Angular | npx -p @angular/cli@22 ng new client | ng build → dist/browser | ng serve --proxy-config proxy.conf.js | proxy.conf.js (JavaScript) | 4200 |
| React (Vite) | npm create vite@latest client -- --template react-ts | npm run build → dist | npm run dev | server.proxy in vite.config.ts | 5173 |
| Vue (Vite) | npm create vue@latest client | npm run build → dist | npm run dev | server.proxy in vite.config.ts | 5173 |
| None (vanilla HTML/CSS/JS) | write .html / .css / .js by hand | none — the files are the output | none (or a static live-reload server) | n/a | n/a |
Notes:
- Single-origin — after building, copy the output into the directory the backend serves (for example, ASP.NET Core's
wwwroot, or a static folder served by Express). Build with a relative base path (Angular:--base-href ./; Vite:base: './') so assets load correctly under CoderFlow's task-proxy path. - Two-process — the proxy forwards
/apito the backend, and the Launch URL points at the dev-server port. Point the proxy target at the backend's port (and allow overriding it with an environment variable so the same config works locally and in a container). - None (vanilla HTML/CSS/JS) — there's no build step, so single-origin has no rebuild friction: the backend serves the files as written, and an edit shows on the next refresh. Two-process doesn't apply unless you add a static live-reload server (for example
live-serverorbrowser-sync). For a site with no backend at all, serve the folder directly withnpx serveorpython -m http.server— the simplest possible environment, with no runtime to install.
Mixing and matching
To assemble any combination, take one backend row and one front-end row and drop their values into the environment pattern:
- pre-clone (Build → Scripts) — the backend's runtime install (skip for Node.js/Python).
- Post-clone Action (Repositories tab, per repo) — install that repo's dependencies (
dotnet restore,npm ci, …; use Custom command to cover both backend and front end). - post-clone (Build → Scripts) — optional: a build step that spans repos or isn't tied to one.
- setup.sh (Build → Scripts) — single-origin only: build the front end into the folder the backend serves (runs at container start, against the task's code).
- Application Server — the start command(s) and ports for your serving model.
For example, Java + React + two-process: the pre-clone script installs a JDK and Maven; the Post-clone Action runs mvn -q package -DskipTests && npm ci (a Custom command); the Start Command runs java -jar target/app.jar & then npm run dev; Ports are 8080 and 5173; the Launch URL is / on 5173.