Deploying logstream-server on Windows

This guide covers a full manual deployment of the logstream-server on a Windows machine (Windows 10/11 or Windows Server 2019/2022), running as a background Windows Service using NSSM.


Prerequisites

  • Windows 10/11 or Windows Server 2019/2022 (64-bit)
  • Administrator privileges
  • The repository URL: https://github.com/guibranco/logstream-server
  • A domain name with an A record pointing to your server’s public IP (for HTTPS)
  • Ports 8080 and 8081 available (or configure alternates in .env)

1. Install PHP 8.3

Download

Go to https://windows.php.net/download and download the PHP 8.3 Non-Thread Safe (NTS) x64 ZIP archive.

Use the NTS build — the service runs a single long-lived CLI process, not Apache/IIS.

Install

# Run PowerShell as Administrator

# Create the PHP directory
New-Item -ItemType Directory -Path "C:\php8.3" -Force

# Extract the downloaded ZIP to C:\php8.3
Expand-Archive -Path "$env:USERPROFILE\Downloads\php-8.3.x-nts-Win32-vs16-x64.zip" `
               -DestinationPath "C:\php8.3"

Configure php.ini

# Copy the example ini file
Copy-Item "C:\php8.3\php.ini-production" "C:\php8.3\php.ini"

# Open it for editing
notepad "C:\php8.3\php.ini"

Find and uncomment (remove the ;) the following lines:

extension_dir = "ext"
extension=curl
extension=fileinfo
extension=mbstring
extension=openssl
extension=pdo_mysql
extension=sockets
extension=zip

Also set the timezone:

date.timezone = Europe/Dublin

Add PHP to the system PATH

# Add C:\php8.3 to the system PATH permanently
[Environment]::SetEnvironmentVariable(
    "PATH",
    $env:PATH + ";C:\php8.3",
    [EnvironmentVariableTarget]::Machine
)

Close and reopen PowerShell, then verify:

php --version

2. Install Composer

Download and install

# Download the Composer installer
Invoke-WebRequest -Uri "https://getcomposer.org/Composer-Setup.exe" `
                  -OutFile "$env:TEMP\Composer-Setup.exe"

# Run the installer (GUI — follow the prompts, point it to C:\php8.3\php.exe)
Start-Process "$env:TEMP\Composer-Setup.exe" -Wait

The installer adds Composer to the PATH automatically.

Verify:

composer --version

3. Install Git

Download and install Git for Windows from https://git-scm.com/download/win.

Accept all defaults during installation.

Verify:

git --version

4. Clone the repository

# Run PowerShell as Administrator

# Create the application directory
New-Item -ItemType Directory -Path "C:\logstream-server" -Force

# Clone the repository
git clone https://github.com/guibranco/logstream-server.git C:\logstream-server

Private repository? Configure a personal access token:

git clone https://<YOUR_TOKEN>@github.com/guibranco/logstream-server.git C:\logstream-server

5. Configure the environment

# Copy the example env file
Copy-Item "C:\logstream-server\.env.example" "C:\logstream-server\.env"

# Open it for editing
notepad "C:\logstream-server\.env"

Key values to set:

HTTP_PORT=8081
WS_PORT=8080

# Write key — used by your applications to POST logs
API_SECRET=<generate-a-strong-secret>

# Read key — used by the UI and authorised humans to view logs
UI_SECRET=<generate-another-strong-secret>

# Storage: "file" needs no extra setup; "mariadb" requires the DB section below
STORAGE_TYPE=file
LOG_PATH=C:\logstream-server\storage\logs

Generate strong secrets in PowerShell:

[System.Web.Security.Membership]::GeneratePassword(32, 4)
# or
-join ((65..90) + (97..122) + (48..57) | Get-Random -Count 32 | % {[char]$_})

6. Install PHP dependencies

cd C:\logstream-server

composer install --no-dev --optimize-autoloader --no-interaction --no-progress

7. Create the log storage directory

Only needed when using STORAGE_TYPE=file:

New-Item -ItemType Directory -Path "C:\logstream-server\storage\logs" -Force

8. (Optional) MySQL / MariaDB setup

Skip this section if you are using STORAGE_TYPE=file.

Install MariaDB

Download MariaDB from https://mariadb.org/download and run the MSI installer. Note the root password you set during installation.

Create the database and user

Open the MariaDB command prompt (Start → MariaDB → MySQL Client):

CREATE DATABASE logservice CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'logservice'@'127.0.0.1' IDENTIFIED BY 'a-strong-db-password';
GRANT ALL PRIVILEGES ON logservice.* TO 'logservice'@'127.0.0.1';
FLUSH PRIVILEGES;
EXIT;

Run the migration

mysql -h 127.0.0.1 -u logservice -p logservice `
    < C:\logstream-server\migrations\001_logs.sql

Update the .env file

STORAGE_TYPE=mariadb
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=logservice
DB_USER=logservice
DB_PASS=a-strong-db-password

9. Test the server manually first

Before installing as a service, confirm the server starts:

cd C:\logstream-server
php bin\server.php

You should see:

[Storage] File (C:\logstream-server\storage\logs)
╔══════════════════════════════════════════╗
║           LogService started             ║
╠══════════════════════════════════════════╣
║  HTTP API  →  http://0.0.0.0:8081       ║
║  WebSocket →  ws://0.0.0.0:8080         ║
╠══════════════════════════════════════════╣
║  Write key (API_SECRET)  : ✅ set       ║
║  Read key  (UI_SECRET)   : ✅ set       ║
╚══════════════════════════════════════════╝

In a second PowerShell window, verify the health endpoint:

Invoke-RestMethod http://localhost:8081/api/health | ConvertTo-Json

Press Ctrl+C to stop the server before continuing.


10. Install NSSM (Non-Sucking Service Manager)

NSSM wraps any executable as a proper Windows Service with restart-on-failure support.

# Download NSSM
Invoke-WebRequest -Uri "https://nssm.cc/release/nssm-2.24.zip" `
                  -OutFile "$env:TEMP\nssm.zip"

# Extract it
Expand-Archive -Path "$env:TEMP\nssm.zip" -DestinationPath "$env:TEMP\nssm"

# Copy the 64-bit binary to a permanent location
New-Item -ItemType Directory -Path "C:\tools" -Force
Copy-Item "$env:TEMP\nssm\nssm-2.24\win64\nssm.exe" "C:\tools\nssm.exe"

# Add C:\tools to the system PATH
[Environment]::SetEnvironmentVariable(
    "PATH",
    $env:PATH + ";C:\tools",
    [EnvironmentVariableTarget]::Machine
)

Reopen PowerShell and verify:

nssm version

11. Register logstream-server as a Windows Service

# Run PowerShell as Administrator

# Register the service
nssm install logstream-server "C:\php8.3\php.exe"

# Set startup arguments
nssm set logstream-server AppParameters "C:\logstream-server\bin\server.php"

# Set the working directory (important for .env loading)
nssm set logstream-server AppDirectory "C:\logstream-server"

# Redirect stdout and stderr to log files
nssm set logstream-server AppStdout "C:\logstream-server\storage\service-stdout.log"
nssm set logstream-server AppStderr "C:\logstream-server\storage\service-stderr.log"
nssm set logstream-server AppRotateFiles 1
nssm set logstream-server AppRotateBytes 10485760

# Restart automatically on failure, after 5 seconds
nssm set logstream-server AppThrottle 5000
nssm set logstream-server AppRestartDelay 5000

# Set the service description
nssm set logstream-server Description "LogStream Server — HTTP API and WebSocket log service"

# Start the service
nssm start logstream-server

# Confirm it is running
nssm status logstream-server

Configure the service to start automatically

Set-Service -Name logstream-server -StartupType Automatic

12. Managing the service

# Start
nssm start logstream-server

# Stop
nssm stop logstream-server

# Restart
nssm restart logstream-server

# View current status
nssm status logstream-server

# Edit service settings (opens NSSM GUI)
nssm edit logstream-server

# Uninstall the service entirely
nssm remove logstream-server confirm

Alternatively using the built-in sc command:

Start-Service logstream-server
Stop-Service  logstream-server

13. Open Windows Firewall ports

Allow inbound traffic on the HTTP and WebSocket ports (or just 443 if using a reverse proxy):

# Allow HTTP API port
New-NetFirewallRule -DisplayName "LogStream HTTP API" `
    -Direction Inbound -Protocol TCP -LocalPort 8081 -Action Allow

# Allow WebSocket port
New-NetFirewallRule -DisplayName "LogStream WebSocket" `
    -Direction Inbound -Protocol TCP -LocalPort 8080 -Action Allow

14. (Optional) Nginx reverse proxy with HTTPS on Windows

For production use you should put Nginx in front to handle HTTPS.

Install Nginx for Windows

Download the stable release from https://nginx.org/en/download.html and extract to C:\nginx.

Install as a service with NSSM

nssm install nginx "C:\nginx\nginx.exe"
nssm set nginx AppDirectory "C:\nginx"
nssm start nginx

Obtain an SSL certificate with win-acme

win-acme is the Windows equivalent of Certbot:

# Download win-acme from https://www.win-acme.com
# Extract to C:\win-acme and run:
C:\win-acme\wacs.exe

Follow the interactive prompts to issue a certificate for your domain.

Configure Nginx

Edit C:\nginx\conf\nginx.conf:

worker_processes 1;

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        server_name logs.yourdomain.com;
        return 301 https://$host$request_uri;
    }

    server {
        listen 443 ssl;
        server_name logs.yourdomain.com;

        ssl_certificate     C:/win-acme/certs/logs.yourdomain.com/fullchain.pem;
        ssl_certificate_key C:/win-acme/certs/logs.yourdomain.com/privkey.pem;

        location /api/ {
            proxy_pass         http://127.0.0.1:8081;
            proxy_http_version 1.1;
            proxy_set_header Host              $host;
            proxy_set_header X-Real-IP         $remote_addr;
            proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        location /ws {
            proxy_pass         http://127.0.0.1:8080;
            proxy_http_version 1.1;
            proxy_set_header Upgrade    $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout  3600s;
            proxy_send_timeout  3600s;
        }
    }
}

Reload Nginx after any config change:

C:\nginx\nginx.exe -s reload

Block the raw ports from outside once Nginx is in front:

# Remove the rules we added in step 13
Remove-NetFirewallRule -DisplayName "LogStream HTTP API"
Remove-NetFirewallRule -DisplayName "LogStream WebSocket"

# Allow only HTTP and HTTPS through the firewall
New-NetFirewallRule -DisplayName "HTTP"  -Direction Inbound -Protocol TCP -LocalPort 80  -Action Allow
New-NetFirewallRule -DisplayName "HTTPS" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow

15. Final end-to-end verification

# Health check
Invoke-RestMethod http://localhost:8081/api/health

# Send a test log entry
$body = @{
    app_key  = "deploy-test"
    app_id   = "windows"
    level    = "info"
    category = "deployment"
    message  = "Server deployed successfully on Windows"
} | ConvertTo-Json

Invoke-RestMethod `
    -Uri "http://localhost:8081/api/logs" `
    -Method POST `
    -Headers @{
        "Authorization" = "Bearer <API_SECRET>"
        "Content-Type"  = "application/json"
        "User-Agent"    = "PowerShellTest/1.0"
    } `
    -Body $body

# Read it back
Invoke-RestMethod `
    -Uri "http://localhost:8081/api/logs?app_key=deploy-test" `
    -Headers @{ "Authorization" = "Bearer <UI_SECRET>" }

Day-to-day operations

View service logs

# Tail the stdout log
Get-Content "C:\logstream-server\storage\service-stdout.log" -Wait -Tail 50

Deploy a new version manually

cd C:\logstream-server

git pull origin main

composer install --no-dev --optimize-autoloader --no-interaction

nssm restart logstream-server

# Confirm healthy
Invoke-RestMethod http://localhost:8081/api/health

Run database migrations (MariaDB only)

mysql -h 127.0.0.1 -u logservice -p logservice `
    < C:\logstream-server\migrations\001_logs.sql

Troubleshooting

Symptom Likely cause Fix
php not found in PowerShell PHP not in PATH Add C:\php8.3 to system PATH and reopen PowerShell
The specified module could not be found PHP error DLL dependency missing Install Visual C++ Redistributable
sockets extension error Extension not enabled Uncomment extension=sockets in C:\php8.3\php.ini
Service starts then immediately stops PHP error on startup Check C:\logstream-server\storage\service-stderr.log
Port already in use Another process on 8080/8081 Run netstat -ano | findstr :8081 to find the PID
composer: command not found Composer installer did not update PATH Reopen PowerShell or re-run the Composer installer
401 Unauthorized on POST /api/logs Wrong or missing API_SECRET Check Authorization: Bearer <API_SECRET> header
401 Unauthorized on GET /api/logs Wrong or missing UI_SECRET Check Authorization: Bearer <UI_SECRET> header
WebSocket connection rejected Wrong or missing token Connect with wss://domain/ws?token=<UI_SECRET>