Runtime Applications
Deploy and manage Node.js, Python, Ruby, .NET, and Docker applications alongside your PHP websites.
Overview
Runtime apps let you run long-running processes (web servers, APIs, workers) on a domain. When you create a runtime app, Pinkerver:
- Allocates a unique port (10000-60000 range)
- Creates a systemd service unit (
pinkerver-rt-{id}.service) - Configures NGINX as a reverse proxy from the domain to
127.0.0.1:{port} - Writes an environment file with your custom vars +
PORT
Traffic Flow
Installing Runtime Versions
Before creating apps, install the runtime versions you need. Go to Runtime Versions in the sidebar.
Click Install on any version to begin. A progress dialog shows real-time installation output. Installations run in the background and typically take 1-3 minutes.
| Runtime | Available Versions | Package Source |
|---|---|---|
| Node.js | 18, 20, 22 (LTS) | NodeSource APT repository |
| Python | 2.7, 3.10-3.13 | System APT + deadsnakes PPA |
| Ruby | 3.1, 3.2, 3.3 | System APT or ruby-install |
| .NET | 6.0, 8.0, 9.0 | Microsoft APT repository |
| Docker | latest | Docker official APT repository |
Node.js
| Field | Description | Example |
|---|---|---|
| Entry Point | Main JS file to execute | app.js |
| Process Mode | fork (single) or cluster (multi via PM2) | fork |
| Instances | Number of cluster workers (cluster mode only) | 2 |
Fork mode runs your app with node {entry_point}. Your app must listen on the PORT environment variable:
// app.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => res.send('Hello World'));
app.listen(port, '127.0.0.1', () => {
console.log(`Listening on port ${port}`);
});
Cluster mode uses PM2 runtime: npx pm2-runtime start {entry_point} -i {instances}. Your app code stays the same — PM2 handles the forking.
npm install in your domain's document root before starting the app.
Python
| Field | Description | Example |
|---|---|---|
| Entry Point | WSGI/ASGI module path | app:app |
| Server | gunicorn (WSGI) or uvicorn (ASGI) | gunicorn |
| Workers | Number of worker processes | 2 |
A Python virtual environment (.venv) is automatically created in the app root with the selected server installed.
Flask Example
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello World'
Entry point: app:app
FastAPI Example
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def read_root():
return {'message': 'Hello World'}
Entry point: main:app, Server: uvicorn
.venv/bin/pip install -r requirements.txt
Python 2.7 Legacy
Python 2.7 is available for legacy applications that haven't been migrated to Python 3. It is marked with a Legacy badge in the version list.
Key differences from Python 3.x:
- No virtual environment is created (Python 2 lacks the
venvmodule) - Dependencies must be installed system-wide or managed manually with
virtualenv
Ruby
| Field | Description | Example |
|---|---|---|
| Entry Point | Rack config file | config.ru |
| Server | puma or passenger | puma |
| Workers | Number of worker processes | 2 |
Sinatra Example
# config.ru
require 'sinatra'
get '/' do
'Hello World'
end
run Sinatra::Application
bundle install in the document root before starting the app.
.NET
| Field | Description | Example |
|---|---|---|
| Entry Point | Compiled DLL to run | MyApp.dll |
The startup command runs: dotnet {entry_point} --urls http://127.0.0.1:$PORT
Building & Deploying
- On your development machine, publish your app:
dotnet publish -c Release -o ./publish - Upload the
publish/folder contents to your domain's document root - Set the entry point to your main DLL (e.g.,
MyApp.dll)
Minimal API Example
// Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World");
app.Run();
--urls, which Pinkerver sets automatically. You don't need to configure Kestrel URLs in your app.
Docker
| Field | Description | Example |
|---|---|---|
| Docker Image | Image name with optional tag | nginx:latest |
The container must listen on $PORT internally.
Managing Apps
| Action | Description |
|---|---|
| Start | Starts the systemd service |
| Stop | Stops the service gracefully |
| Restart | Restarts the service (useful after code changes) |
| Edit | Change name, entry point, env vars, server settings |
| Logs | View recent stdout/stderr output |
| Delete | Stops service, removes NGINX proxy, deletes all config |
Status Indicators
Environment Variables
Add custom environment variables in the create/edit form. These are written to /etc/pinkerver/runtime-env/{id}.env and loaded by systemd.
The PORT variable is always set automatically — do not override it.
NODE_ENV=production
DATABASE_URL=mysql://user:pass@localhost/mydb
SECRET_KEY=your-secret-here
Changes to environment variables take effect after restarting the app.
Logs
Click the terminal icon on any runtime app to view logs. Logs auto-refresh every 3 seconds. The last 200 lines are shown by default.
For more detailed debugging via SSH:
journalctl -u pinkerver-rt-{id} -f