FiveM & QBCore · Practical guide

How to Add a Mining Job to Your QBCore FiveM Server

Mining is a popular activity on FiveM servers. Players dig for ore, process it and sell it for income. This guide covers setting up mining on QBCore.

Stellar AI · Updated 8 September 2026 · 6 min read

A practical guide to how to add a mining job to your qbcore fivem server, with implementation decisions, validation steps, and security considerations for a production-minded project.

Overview

This guide shows how to add a robust, secure mining job to a QBCore FiveM server. It focuses on architecture, server-side validation, implementation choices (qb-target / ox_target / ox_lib), database modeling, testing, deployment, and ongoing maintenance. Follow the patterns here to avoid trusting any client input, to keep server authority intact, and to make the job maintainable and scalable.

Architecture and Data Model

Design your mining job with clear server authority and minimal client responsibility. Typical components:

  • Client: UI, targeting interactions, animation triggers, local visual effects only.
  • Server: authoritative logic for harvest, inventory, rewards, cooldowns, anti-cheat, and persistence.
  • Database: persistent player progress, optionally rock respawn schedules, and audit logs.
  • Third-party libs: qb-target or ox_target for interactions; ox_lib for menu builders if needed; ghmattimysql or mysql-async for persistence.

Minimal DB schema example:


CREATE TABLE IF NOT EXISTS mining_logs (
  id INT AUTO_INCREMENT PRIMARY KEY,
  steam HEX(16),
  player_id INT,
  action VARCHAR(64),
  item VARCHAR(64),
  amount INT,
  timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  

Implementation Choices and fxmanifest

Decide whether to implement as a standalone resource or integrate into an existing job resource. Example fxmanifest essentials:


fx_version 'cerulean'
game 'gta5'

author 'Your Name'
version '1.0.0'

shared_script 'shared.lua'
server_script 'server.lua'
client_script 'client.lua'

dependencies {
  'qb-core'
}
  

If using qb-target or ox_target, declare them as dependencies or conditionally detect them in runtime to register interaction zones appropriately.

Server-Side Job Flow (Secure)

The server must validate every client request. Do not assume client-sent IDs, coordinates, or durations are genuine. The canonical flow:

  1. Player triggers the target interaction client-side (no sensitive state changed).
  2. Client fires a server event like qb-mining:server:StartMine with a rock ID.
  3. Server verifies the rock ID is allowed, checks cooldowns/locks, checks inventory capacity, and then sets a server-side lock for that rock or player.
  4. Server returns an optimistic confirmation to client (e.g., start animation) but does not grant items yet.
  5. After the expected mining duration, client notifies server with a request to complete (or server times out and resolves itself via server tick).
  6. Server re-validates and grants items or denies and logs suspicious activity.

Example server-side skeleton:


-- server.lua
local QBCore = exports['qb-core']:GetCoreObject()
local rockLocks = {} -- [rockId] = source or true

RegisterNetEvent('qb-mining:server:StartMine', function(rockId)
  local src = source
  local Player = QBCore.Functions.GetPlayer(src)
  if not Player then return end

  -- Validate rock and availability on server list
  if not ValidRock(rockId) or rockLocks[rockId] then
    TriggerClientEvent('qb-mining:client:MiningDenied', src, 'Rock unavailable.')
    return
  end

  -- Inventory capacity check
  if not Player.Functions.CanCarryItem('stone', 1) then
    TriggerClientEvent('qb-mining:client:MiningDenied', src, 'No inventory space.')
    return
  end

  -- Lock and start a server-side timer
  rockLocks[rockId] = src
  SetTimeout(8000, function()
    -- Re-validate and reward
    local p = QBCore.Functions.GetPlayer(src)
    if p and rockLocks[rockId] == src then
      p.Functions.AddItem('stone', 1) -- authoritative
      ExportLogMining(p, 'stone', 1)
    end
    rockLocks[rockId] = nil
  end)
end)
  

Client Integration: Targets and Animations

Client code only triggers UI and local effects. Use qb-target or ox_target to present interaction points. Never let client code directly add items.


-- client.lua (qb-target)
local target = exports['qb-target']

target:AddBoxZone("mining_rock_1", vector3(2856.0, 2790.0, 40.0), 1.0, 1.0, {
  name="mining_rock_1",
  heading=0,
  debugPoly=false,
}, {
  options = {
    {
      type = "client",
      event = "qb-mining:client:TryMine",
      icon = "fas fa-hammer",
      label = "Mine Rock",
      rockId = 1
    }
  },
  distance = 2.5
})

RegisterNetEvent('qb-mining:client:TryMine', function(data)
  -- Play animation locally
  -- request server to start mining
  TriggerServerEvent('qb-mining:server:StartMine', data.rockId)
end)
  

If you detect multiple target systems, prefer a conditional loader and make the resource compatible with both. Do not send coordinates or other sensitive state that the server must trust.

Inventory Items, Shops, and Processing

Create item definitions and processing recipes in your shared or database data. Example items:

  • stone (raw)
  • ore (raw ore for smelting)
  • gem (rare drop)

Processing should be server-side too: when a player uses a furnace, server checks for required inputs, removes them server-side, and adds outputs. If you want a cooldown or labor animation, use server-side timers as shown previously.

Security and Server Validation

Security is paramount. Do not trust any client payload. Key patterns:

  • Validate rock IDs against a server-side table. Do not accept arbitrary IDs.
  • Check player inventory and weight capacity server-side before granting items.
  • Add per-player and per-rock cooldowns and rate limits to prevent farming via rapid events.
  • Log suspicious behavior: repeated failed attempts, mismatched client/server timers, or malformed payloads.
  • Use QBCore.Functions.GetPlayer(source) and server callbacks for authoritative state.

Example validation checklist

  • Is rockId in allowed set?
  • Is rock locked by another player?
  • Does player have inventory space?
  • Has player been rate-limited?
  • Has the operation been logged for auditing?

Testing and Debugging

Test thoroughly in a staging server. Steps:

  1. Enable verbose server logging for the module and run automated simulated actions.
  2. Test edge cases: full inventory, multiple clients attempting the same rock, network latency simulation.
  3. Force failure paths: make DB unavailable to confirm graceful degradation and retries.
  4. Confirm logs capture: player id, steam hex, action, result.

Useful debug snippet for server logging:


local function ExportLogMining(player, item, amount)
  print(("MINING LOG - %s (%s): %s x%d"):format(player.PlayerData.citizenid, player.PlayerData.steam, item, amount))
  -- optionally persist to DB here
end
  

Deployment and Maintenance

Deployment checklist and maintenance considerations are about safe rollouts and migrations. Use your resource manifest versioning, back up player inventories and DB tables, and validate migration scripts before applying on production. If you need orchestration or documentation for staff, integrate the resource with your admin tools.

For quick ops and team access tools, consider pairing that documentation with team platforms such as the Stellar AI project manager: https://trystellarai.com/app. When coordinating deploy windows and changelogs, keep a copy of DB migration scripts checked into version control and maintain a rollback plan. For planning features and tracking issues, you can centralize task boards using: https://trystellarai.com/app.

Performance and Scaling

Mines are event-driven but can create spikes if many players mine simultaneously. Mitigation:

  • Batch DB writes for logs using a worker or buffer with a reasonable flush interval.
  • Keep rock state in memory and persist only essential changes or periodic snapshots.
  • Rate limit per-player mining attempts and global concurrent mines.

Roblox / Luau Considerations (Brief)

If you are implementing a similar mining job in Roblox, server authority patterns are similar: RemoteEvents are used for client->server requests, but the server must validate everything. On the Roblox server:

  • Validate player permissions, item capacity, and object availability server-side.
  • Use DataStore with careful failure handling: implement exponential backoff, limit retries, and consider a local cache to avoid repeated calls on short failures.
  • Handle DataStore rate limits by batching transforms and persisting only changes or snapshots at safe intervals.

Always treat RemoteEvents as untrusted input and check all fields before applying changes to a DataStore or updating a player's inventory.

Practical Deployment Checklist

Step Action Completed
1 Create resource skeleton and fxmanifest ☐
2 Implement server validation and item grants ☐
3 Register items in shared item list ☐
4 Set up target interactions (qb-target / ox_target) ☐
5 Write DB migration / logs table ☐
6 Staging tests: concurrency, inventory edge cases ☐
7 Production rollout with monitoring and rollback plan ☐

Long-Term Maintenance

Maintain changelogs, add automated alerting for suspicious activity, and rotate audit logs. Periodically review spawn rates and economy impact of the mining job. If you add new items or processing, add DB migrations and test them on staging first.

Further Reading and Blog

For deeper operational or design perspectives, see the developer blog post collection: https://trystellarai.com/blog.

Build your next system with Stellar AI

Describe one feature, get organized project files, then bring back your errors to keep improving. Start free with no card required. Test generated code in a private development environment before release.

Create your first script free →