Self-hosting
Deploy on Windows
Build FLICKED and run it somewhere your friends can reach: one Windows VPS running the CS2 server, the backend and the dashboard together.
Everything works the same spread across several machines; the addresses just stop being the same one. On Linux? Deploy on Linux covers the same deployment with systemd and HTTPS in the main path.
What you need#
| Requirement | Notes |
|---|---|
| A Windows VPS | The one already running your CS2 server is fine |
| .NET 10 SDK | On your development machine only. The VPS needs nothing |
| Node.js LTS | On both, if you want the dashboard |
| A Steam Web API key | Free, from steamcommunity.com/dev/apikey |
| Your SteamID64 | So you can reach the dashboard as an admin |
The three addresses#
Nearly every deployment problem is one of these being wrong, and each fails quietly.
a friend's PC your VPS Steam
───────────── ──────── ─────
launcher ──── http ────► Flicked.Api :5165 ──── RCON ────► CS2 + MatchZy
│ │ ▲ │
│ Postgres └────── match events ─────────┘
│
└──── steam://connect ──────────────────────────────────► CS2 server| Setting | Who reads it | If it is wrong |
|---|---|---|
Steam:PublicUrl | A friend's browser, signing in | Sign-in sends them back to their own PC |
Api:PublicUrl | The CS2 server, fetching a match config | The server fetches nothing and stays on its old map, with no error anywhere |
FLICKED_API | The launcher, compiled in | The launcher looks for FLICKED on the player's own machine |
All three must be your VPS address. Replace <vps-address> below with it.
Part 1: Build#
All three are built on your development machine. Nothing is compiled on the VPS.
The API#
dotnet publish backend/Flicked.Api -c Release -r win-x64 --self-containedOutput lands in backend/Flicked.Api/bin/Release/net10.0/win-x64/publish/.
--self-contained bundles the .NET runtime, so the VPS needs no SDK. It is about 115 MB the first
time; later updates are two files.
The launcher#
The API address is compiled in, so a build made on your machine points at your machine unless you say otherwise:
set FLICKED_API=http://<vps-address>:5165
cd launcher
npm install
npm run tauri buildInstallers land in launcher/src-tauri/target/release/bundle/: an .exe (NSIS) and an
.msi, the same app either way.
Friends will see "Windows protected your PC" when they run it. That is SmartScreen reacting to an unsigned installer, not to anything being wrong: More info, then Run anyway. Signing needs a paid certificate.
The dashboard#
cd dashboard
npm install
set NEXT_PUBLIC_API_URL=http://<vps-address>:5165
npx next buildThe build produces a standalone server. Assemble what the VPS needs:
.next/standalone/* → the server and its dependencies
.next/static/* → into <deploy>/.next/static
public/* → into <deploy>/publicThat folder runs with Node alone, with no npm install on the VPS.
Part 2: Deploy#
-
Install Postgres
Docker Desktop on a Windows VPS usually cannot get the nested virtualization it needs, so install Postgres natively with the EnterpriseDB installer. It runs as a service and starts with the machine. Then, in SQL Shell (psql):
psqlCREATE USER flicked WITH PASSWORD 'pick-a-real-password'; CREATE DATABASE flicked OWNER flicked;Create no tables. The API applies its own migrations on startup and seeds the map pool.
-
Copy the API across
Put the published folder anywhere on the VPS. These examples use
C:\flicked\api. -
Configure it
Create
.envnext toFlicked.Api.exe. It is read on startup and belongs in no repository. Every key is described in Configuration.C:\flicked\api\.env# The database you just created. ConnectionStrings:Flicked=Host=localhost;Port=5432;Database=flicked;Username=flicked;Password=<db-password> # This machine, as the outside world sees it. Never localhost: one of these is # read by a browser on someone else's PC, the other by the CS2 server. Steam:PublicUrl=http://<vps-address>:5165 Api:PublicUrl=http://<vps-address>:5165 # Where the dashboard runs. Needed for CORS and for the sign-in redirect. Dashboard:Url=http://<vps-address>:3000 # Listen on every interface. The default is localhost only, which leaves the # API invisible from outside however the firewall is set. urls=http://0.0.0.0:5165 STEAM_API_KEY=<your-steam-web-api-key> # Comma-separated SteamID64s allowed into the dashboard. FLICKED_ADMIN_STEAM_IDS=<your-steamid64> # Encryption keys for stored RCON passwords. Back this folder up. DataProtection:KeyPath=C:\flicked\keys # Optional, while testing: play with fewer people than a real match needs. # Matchmaking:CompetitivePlayers=2 # Optional, for a small community: start short-handed rather than wait for a # tenth player who is not coming. # Matchmaking:MinCompetitivePlayers=6 -
Open the ports
In an administrator PowerShell on the VPS:
PowerShell (admin)New-NetFirewallRule -DisplayName "FLICKED API" -Direction Inbound -Protocol TCP -LocalPort 5165 -Action Allow New-NetFirewallRule -DisplayName "FLICKED Dashboard" -Direction Inbound -Protocol TCP -LocalPort 3000 -Action AllowMany providers have a second firewall in their web panel. If a port still looks closed, check there.
-
Start the API
Run
Flicked.Api.exe. The first start applies the migrations; watch that it gets through them without an exception. Then test it from your own PC, not from the VPS, because a service can answer itself while being unreachable from outside:Your PCcurl http://<vps-address>:5165/api/news -
Start the dashboard
PowerShellcd C:\flicked\dashboard $env:PORT=3000; $env:HOSTNAME="0.0.0.0"; node server.jsHOSTNAME=0.0.0.0matters for the same reasonurlsdid: Next binds to localhost otherwise. -
Add your CS2 server
Open
http://<vps-address>:3000, sign in with Steam, and add the server with its host, port and RCON password. FLICKED handles the rest: it reloads MatchZy before each match, and sends the config URL and event settings over RCON, soserver.cfgneeds no FLICKED-specific lines.A server that answers the pool's RCON ping shows as Idle and can host matches. One that does not is Offline and is skipped until it answers. For the game side, see Running the CS2 server.
Keep it running#
A console window dies when you log out of the VPS. Install NSSM and register the API as a service:
nssm install FlickedApi C:\flicked\api\Flicked.Api.exe
nssm set FlickedApi AppDirectory C:\flicked\api
nssm start FlickedApiUpgrading#
After the first deployment you rarely need the whole 115 MB again. Unless the dependencies changed, a new backend build is two files:
dotnet publishas above, on your machine- stop the API on the VPS
- copy
Flicked.Api.dllandFlicked.Api.pdbover the old ones - start it; any new migrations apply on startup
Back up the database first when an upgrade migrates data. From PowerShell, not psql, since
pg_dump is a program rather than a psql command:
$pg = Get-ChildItem "C:\Program Files\PostgreSQL\*\bin\pg_dump.exe" | Select-Object -First 1
& $pg.FullName -U postgres -d flicked -f C:\backup.sqlTroubleshooting#
Every one of these has happened during a real setup.
| Symptom | Cause |
|---|---|
| API unreachable from outside, firewall looks open | urls=http://0.0.0.0:5165 missing, so it bound to localhost |
| Server never loads the voted map, console shows nothing | Api:PublicUrl is localhost or unreachable, so the server fetched its config from itself |
| Sign-in sends a friend back to their own PC | Steam:PublicUrl is localhost |
| News and leaderboard empty in the launcher, everything else fine | The launcher was built without FLICKED_API |
| Dashboard signs in, then every request fails | The dashboard is not on the same host as the API (SameSite=Lax) |
Server X has an unreadable RCON password | The database moved between machines without its DataProtection keys |
| Match timers start part-way through, or sit at zero | The VPS clock or the player's is wrong; sync both |
invalid command \... | You are in psql and typed a program name; programs belong in PowerShell |
The ampersand (&) character is not allowed | Two PowerShell commands on one line; separate them with ; |
-U is not recognized after a quoted path | PowerShell needs & in front of a quoted path to run it |