FiveMFeature FlagsTutorial

Feature Flags: Change Your Server Without Restarting It

Stop restarting your FiveM server to change a value. Feature flags are named, typed values you flip in the NoCloud dashboard — live servers pick them up in seconds, and secrets never reach your players.

NoneM September 19, 2026 6 min read

You know the drill. A script is misbehaving during peak hours, so you SSH into the box, open a config file, change one boolean, and restart the resource. Sixty players get kicked back to the loading screen because you wanted to turn PvP off for twenty minutes.

Or worse — the value lives in a file you have to redeploy, so a two-second decision turns into a git push, a pull on the VPS, and a restart you schedule for 4 AM.

Feature flags fix this. A flag is a named, typed value you change in the NoCloud dashboard. Every server reading it picks up the new value within seconds — no restart, no redeploy, no file edit, nobody kicked.

What Problem Does This Actually Solve?

Anything in your server that is a decision rather than code belongs in a flag. A few things server owners do with them on day one:

  • Kill switches. A new heist script is dropping frames. Flip heists-enabled to false and it stops running — while the server stays up and the players stay connected.
  • Maintenance mode. One flag your scripts check to queue-block, disable saves, or show a banner before you take the server down properly.
  • Gradual rollouts. Ship the redesigned HUD behind new-hud, turn it on for an evening, watch your Discord, turn it off if it goes badly.
  • Live tuning. Paycheck multipliers, drop rates, fuel consumption, max players in a queue. The numbers you tweak constantly and hate restarting for.
  • Event switches. Halloween mode, double-XP weekends, seasonal loot tables — flipped on a schedule you decide, not one you deploy.

The point is not that these are hard to code. It is that today every one of them costs you a restart, and a restart costs you players.

Not Just On/Off Switches

A flag holds one of four types, and the type is validated along with the value — so a number flag can never end up holding a string and breaking your Lua math at 2 AM:

  • Booleantrue or false. The classic kill switch.
  • String — a message of the day, a Discord webhook URL, an active event name.
  • Number — multipliers, limits, intervals, prices.
  • JSON — a whole config object. Your entire economy table in one flag, up to 4 KB.

Secrets Stay on Your Server

This is the part that matters most, so we made it a first-class property of every flag rather than something you have to remember.

Every flag has a runtime, which decides who is allowed to read it:

  • Shared — your server and your players' clients both read it. This is the default.
  • Server only — your server reads it. The value is never published to clients.

Your server holds the API key, so it always receives every flag. Clients only ever receive the shared ones. Reading a server-only flag from a client script or a NUI behaves exactly like reading a flag that does not exist — it returns your fallback. Not obfuscated, not filtered in the browser: the value never leaves your server in the first place.

So if a flag holds a webhook URL, a license key, or an admin password, create it as server only. If it holds "is the new HUD on", leave it shared so your client scripts and NUI can read it too.

How to Use It

Step 1: Create a Flag

Open the dashboard, go to Feature Flags, and hit New Flag. You give it a key, a type, a value, and a runtime.

The key is what your code reads it by — lowercase letters, numbers, hyphens and underscores, like new-hud or pay_multiplier. Keys are immutable on purpose: your servers reference a flag by key, so letting you rename one would silently orphan every server reading it. The name and description are free-form and you can change them whenever you like.

Step 2: Read It on Your Server

Install the CFX SDK resource, and the exports are there. Server-side reads are asynchronous — they serve from memory when the values are current and fetch when they are not:

if exports.nocloud:IsFlagEnabled('new-hud', false) then
    -- roll out the new HUD
end

local maxPlayers = exports.nocloud:GetFlagValue('max-players', 32)
local motd = exports.nocloud:GetFlagValue('motd', 'Welcome')
local economy = exports.nocloud:GetFlagValue('economy')

The second argument is always your fallback — what you get if the flag is missing, holds a different type, or was archived in the dashboard. More on why that matters below.

If you prefer a cleaner API, add the Lua library to your fxmanifest.lua and you get full autocomplete with types in any editor running the Lua Language Server:

server_script '@nocloud/lib/server.lua'

if Cloud.flags:is_enabled('new-hud', false) then
    -- ...
end

local maxPlayers = Cloud.flags:get_value('max-players', 32)

Step 3: Read It on the Client

Your server publishes the shared flags to GlobalState, and the game replicates that to every player on its own. So a client read is a local lookup — no round trip, no request, safe to call inside a tick:

if exports.nocloud:IsFlagEnabled('new-hud') then
    -- ...
end

local motd = exports.nocloud:GetFlagValue('motd', 'Welcome')

A joining player already holds the values before your resource runs a line, because the state bag arrives with the connection rather than being requested.

Step 4: Read It in Your NUI

Your interfaces read through the NUI SDK, which reaches the client script's copy of the replicated state — never the API:

import { NoCloud } from "@nocloud/cfx-nui";

if (await NoCloud.flags.isFlagEnabled("new-hud")) {
  renderNewHud();
}

const theme = await NoCloud.flags.getFlagValue("ui-theme", "dark");

Read once into component state rather than once per render, and read again when the interface reopens if you want it current.

Outside the Game Too

Running a Discord bot, a web panel, or a queue service? The Node.js SDK reads the same flags, with typed readers that check the declared type as well as the value — and it can manage flags too, so your admin panel can flip them without anyone opening the dashboard:

if (await cloud.flags.isEnabled("new-hud")) {
  showNewHud();
}

const maxPlayers = await cloud.flags.getNumber("max-players", 32);
const motd = await cloud.flags.getString("motd", "Welcome");

// Reads default to the shared runtime. Ask for server to see everything.
const webhook = await cloud.flags.getString("webhook-url", undefined, {
  runtime: "server"
});

That default is deliberate: a value you are about to send to a player can never accidentally be a server-only one.

It Can't Break Your Live Server

The whole point of a flag is that you flip it casually, in the middle of a busy night, without thinking hard. That only works if a mistake in the dashboard cannot take the server down. So:

  • Reads never throw. A missing flag, an archived flag, a wrong type, or one this runtime may not read all return your fallback. Deleting a flag in the dashboard can never crash a running script.
  • Archive instead of delete. Archiving pulls a flag from what servers see but keeps the key reserved, and it is reversible. It is the safe way to retire one.
  • Restarts and outages are covered. The last known values are kept on the server host, so a restart serves them immediately and an unreachable API never empties them. You can ask whether you are reading a cached snapshot with exports.nocloud:AreFlagsStale().
  • Billing never changes behaviour. If a subscription lapses and you are over the free allowance, the newest flags lock — but a locked flag keeps serving its published value. Only editing it is blocked.

What It Costs in Requests

Nothing is fetched until a flag is actually read. A server whose players never read a flag makes one request in its life — the first start after installing, so clients are not left reading an empty state bag.

After that, server-side reads serve from memory inside the cache window and go to the API once when that window passes — so a read is usually free and never more than one request per window, no matter how often you call it. Client-side reads are local state bag lookups and cost nothing at all.

Polling only runs while a client is actually reading flags, and stops when the last one goes quiet. If you want to watch for changes rather than read them — a nocloud.flags.updated handler, or a state bag change handler — turn flags.polling.enabled on, since a change nobody reads is a change nothing would otherwise go and find:

AddEventHandler('nocloud.flags.updated', function(values, changes)
    for _, change in ipairs(changes) do
        print(change.key, change.kind, json.encode(change.current))
    end
end)

Every Change Is on the Record

Flags are exactly the kind of thing someone flips during an incident and then disagrees about afterwards. So every change appends an audit entry — who did it, what action, and the full before and after state. Created, renamed, value set, runtime changed, archived, restored, deleted. If PvP was off all Saturday, the log tells you who turned it off and when.

Limits

You get 5 flags for free and 50 with any active subscription — and that means any subscription, not a flags-specific one. If you already pay us for storage, your allowance is already raised.

Values cap at 4 KB serialized with JSON nesting up to 4 levels deep, which is plenty for an economy config and small enough that the payload every server polls stays fast.

Stop Restarting Your Server

Every value in your server that you change more than once is a value you should not be restarting for. Pick the three you touch most often — the kill switch, the multiplier, the event toggle — and move them to flags this week.

Create your first flag on the free plan, or read the feature flags documentation for the full details on types, runtimes, and the audit log.

Questions? Join our Discord community — we're happy to help you get set up.

Ready to get started?

Sign up for free and start using NoCloud on your FiveM server today.