Installation Guide
Step-by-step setup for oxide-radio — dependencies, load order, the radio item, database, and verification.
Step-by-step setup for oxide-radio. If you have never edited a server before, don't worry — every step below tells you exactly what to do and what you should see when it works.
Prerequisites
Required resources
These must already be running on your server. Every framework ships with oxmysql, and most already run ox_lib, so you probably have all three already.
| Resource | Minimum Version | Purpose |
|---|---|---|
ox_lib | Latest | Locale and shared runtime helpers |
oxmysql | Latest | Database access — display names, encrypted frequencies, and this resource's settings |
o-link | Latest | Connects this resource to your framework (jobs, duty, names, inventory, admin checks, notifications, death state) |
Recommended resource (voice audio)
| Resource | Required | Purpose |
|---|---|---|
pma-voice | No, but strongly recommended | This is the voice system that actually carries the radio audio. oxide-radio sits on top of it |
pma-voice is a soft dependency — that means the radio does not refuse to start without it. The radio panel opens, players can tune channels, and the roster works, but no voice is transmitted and everyone sees a small "voice unavailable" banner. Almost every roleplay server already runs pma-voice for its regular voice chat. If yours does, you're set.
If you run pma-voice, leave the voice_enableRadios convar on (it is on by default). If it is turned off, pma-voice disables all radio audio and oxide-radio will correctly report voice as unavailable.
Supported frameworks
Everything is handled through the o-link bridge, so the same resource works on any framework o-link supports.
| Framework | Support |
|---|---|
| QBCore | Full |
| QBX (qbx_core) | Full |
| ESX | Full |
| Custom | Via o-link |
One radio, please
oxide-radio is designed to be the only radio on your server. If you are currently running another radio script (a standalone radio, or a radio built into another job script), turn it off before going live so the two don't fight over pma-voice. See Turn off other radio scripts below.
Installation Steps
Place the resource
Put the oxide-radio folder in your server's resources folder. There is nothing to build — the interface ships already built inside the resource.
Add startup order
Open your server.cfg (the text file that tells your server which resources to start) and add the lines below. See Load Order for the full explanation, but the short version is: o-link starts after your framework and pma-voice, and oxide-radio starts after o-link.
ensure oxmysql
ensure ox_lib
ensure pma-voice
ensure o-link
ensure oxide-radioMake sure the radio item exists
The radio opens when a player uses an inventory item. By default that item is named radio (you can change it later — see Config.Item in Configuration). oxide-radio handles what happens when the item is used; you only need to make sure the item exists in your inventory system and is marked usable.
Good news: many frameworks already ship a radio item out of the box — stock QBCore and stock ox_inventory both include one. Check your inventory first; if a usable radio item is already there, you can skip this step entirely.
If you do need to add it, follow the section for your inventory:
Add the item to qb-core/shared/items.lua:
radio = { name = 'radio', label = 'Radio', weight = 1000, type = 'item', image = 'radio.png', unique = false, useable = true, shouldClose = true, description = 'Handheld two-way radio' },Make sure useable = true — that is what lets a player open the radio by using the item.
Both QBX and ESX servers running ox_inventory use the same file. Add the item to ox_inventory/data/items.lua:
['radio'] = {
label = 'Radio',
weight = 1000,
stack = true,
close = true,
description = 'Handheld two-way radio',
},With ox_inventory you do not set a "useable" flag in the item — o-link registers the use handler for you. On QBX, add the item here in ox_inventory/data/items.lua, not in qbx_core/shared/items.lua (that file is deprecated).
If a radio item is already there, check it for a client block. Many ox_inventory setups ship the item pre-wired to whichever radio script they came with, like this:
['radio'] = {
label = 'Radio',
weight = 1000,
allowArmed = true,
consume = 0,
client = {
event = 'mm_radio:client:use' -- delete these three lines
}
},That client.event sends the item straight to the other radio script, so oxide-radio never sees it being used and the panel never opens. Delete the whole client = { ... } block (leave the rest of the item alone) so the use falls through to o-link. It is an easy one to miss, because the item looks perfectly fine in the inventory.
If your ESX server does not use ox_inventory, add the item to your database's items table:
INSERT INTO items (name, label) VALUES ('radio', 'Radio');Then make sure your inventory treats it as usable. Most ESX inventories mark database items usable automatically once o-link registers the handler; if yours needs a per-item flag, follow your inventory's documentation for registering a usable item named radio.
Item icon: a ready-made radio.png ships in this resource's itemimages/ folder. If your inventory already had a radio item, it already has an icon and you can leave things as they are. If you added a brand-new item, copy itemimages/radio.png into your inventory's image folder (qb-inventory/html/images/ for QBCore, ox_inventory/web/images/ for ox_inventory). Without an icon the item still works — it just shows a blank image.
Review configuration
Do this before the first start. On its first start the resource copies everything in shared/config.lua into the oxide_settings database table, and from then on it reads settings from the database. Editing the .lua file afterward has no effect — you change settings live in-game instead (see Configuration → How Configuration Is Stored). So get the important values right before you first start the resource.
Open shared/config.lua and look over these first:
Config.Item— the inventory item that opens the radio (default'radio'). Change it if your item is named something elseConfig.RequireItem— whether players must carry the item to use the radio (defaulttrue)Config.JobChannels— the restricted channels reserved for jobs. The defaults set up police and EMS channels; edit thejobslists to match the exact job names on your server (see the note below)Config.MinFrequency/Config.MaxFrequency— the range of open civilian frequencies (default 1–9999)Config.OverlayKey— the default key for the on-screen roster overlay (defaultU)
Job names are case-sensitive. In Config.JobChannels, each channel's jobs list must contain the job name exactly as your framework spells it, including capitalization. If your police job is police, list 'police'; if it's LSPD, list 'LSPD'. The defaults include several common spellings so they work on more servers out of the box — trim them down to the ones your server actually uses. See Configuration for the full breakdown.
The full reference for every option is in Configuration.
Turn off other radio scripts
If you were running a different radio resource, stop it so oxide-radio is the only thing talking to pma-voice. Running two radio scripts at once leads to players getting bumped off channels unexpectedly, and both will try to claim the radio item.
The ones you are most likely to have:
| Framework | Resource to turn off | Why it clashes |
|---|---|---|
| QBCore | qb-radio | Registers radio as a usable item itself, so whichever script loads last wins |
| QBX / ESX | mm_radio | Usually paired with the client.event hook on the radio item described in the previous step |
Both normally live in a [voice] folder next to pma-voice. Deleting the ensure line is not enough on its own when the resource sits inside a folder that is ensured as a whole (ensure [voice] starts everything in it). Move the folder out instead — most servers keep a [disabled] folder that is never ensured, which is the tidiest place for it:
resources/[voice]/qb-radio → resources/[disabled]/qb-radioKeep pma-voice where it is. It is the voice system oxide-radio talks to, not a radio script, and it must stay running.
If you run oxide-police and it has its own built-in radio, it detects oxide-radio and stands its own radio down automatically, mirroring its department channels into oxide-radio instead — no action needed on your part.
Load Order
Every Oxide resource follows the same load order in server.cfg: your framework first, then your other scripts, then o-link (the bridge), then the Oxide resources. pma-voice is one of your scripts, so it belongs before o-link.
# 1. Framework — whatever your framework needs (qb-core, es_extended, qbx_core, etc.)
ensure qbx_core
# 2. Scripts — your other resources, including the voice system
ensure oxmysql
ensure ox_lib
ensure pma-voice
# 3. o-link — the bridge. Starts after your framework and scripts, before any Oxide resource.
ensure o-link
# 4. Oxide resources — keep these together (many owners use an [oxide] folder)
ensure oxide-radiooxide-radiomust start aftero-link.pma-voiceshould start beforeoxide-radioso voice is ready when the radio comes up. Ifpma-voicestarts or restarts later,oxide-radiore-connects to it automatically — but starting it first is cleanest.- If you run several Oxide resources, a common tidy setup is to put them all in a folder literally named
[oxide]and start them with a singleensure [oxide].
Database
You do not need to import any SQL by hand. oxide-radio creates its three tables automatically the first time it starts:
oxide_settings— this resource's live configuration (shared with other Oxide resources, one row per setting)radio_prefs— each character's chosen radio display nameradio_encrypted— the password locks on encrypted frequencies (stored salted and hashed, never as plain text)
A copy of all three table definitions ships in sql/install.sql for reference, or in case your database user does not have permission to create tables — in that case, import that file once by hand using your database manager (the tool you use to view and edit your server's database, such as HeidiSQL, phpMyAdmin, or the database panel in your hosting dashboard).
Verification
Start your server and confirm ox_lib, o-link, and oxide-radio all load without errors in the server console.
Watch the console on the first start for a line like:
[INFO][oxide-radio][boot] radio readyIf pma-voice isn't running, you'll also see a clear warning that voice audio is offline — that's expected if you haven't installed it yet.
Join the server, give yourself a radio item, and use it. The radio panel should open. Type a frequency (e.g. 100), press Tune In, and you should see yourself connected on channel 100.
Press U (the default overlay key) while connected — a small list of who's on your channel should appear in the corner. Talk on the radio (using your pma-voice radio key) and your name should light up.
Have a second player tune to the same frequency and talk — you should hear them, and see them highlight when they speak. If you can see them on the roster but can't hear them, that's a pma-voice setup issue, not oxide-radio (see Troubleshooting).
As an admin, run /radio settings in game — the live settings menu should open. This confirms your admin permission is set up.
Optional Setup
Voice audio (pma-voice)
Install and start pma-voice before oxide-radio (see Load Order). Once it's running, the "voice unavailable" banner disappears and radio audio works. The push-to-talk key for the radio is pma-voice's own keybind, which each player can rebind in their FiveM key bindings — oxide-radio does not change it.
Police and EMS channels
If you run oxide-police or oxide-ems, you do not configure their radio channels here. When those resources are installed they register their department channels with oxide-radio automatically, and officers land on the correct frequency when they clock on, with their badge number shown on the roster. You manage those channels inside oxide-police / oxide-ems's own admin menus. The Config.JobChannels in oxide-radio are for jobs that don't have their own resource doing this.