Skip to content

The Edge of the Cyber World See the latest

Apps

osquery Endpoint Monitoring Setup: 13 Steps, 60 Min [2026]

osquery turns every laptop, server, and container host into a queryable SQL database. Instead of grepping log files or SSHing into a box to check what processes are running, you write a SELECT statement. Meta built it internally, open-sourced it in 2014, and it now sits under the osquery Foundation inside the Linux Foundation, licensed Apache 2.0. The current stable release is osquery 5.23.1, shipped June 24, 2026 according to the project’s GitHub releases page, and it patches two Windows privilege-escalation bugs — CVE-2026-54000 and CVE-2026-54001 — that affected every build before it. If you are still running anything older, that alone is reason to rebuild.

This tutorial walks through a full osquery deployment: installing the agent on Ubuntu, writing a real configuration and query pack, running it as a systemd service, shipping logs to a SIEM, and layering on Fleet (fleetdm) for centralized management across more than one host. By the end you will have a working endpoint-monitoring setup you can point at a lab VM today and a production fleet next week.

The appeal of osquery over a bespoke shell-script agent comes down to a single idea: SQL is a language most engineers already know, and a query is far easier to review, version-control, and reason about than a pile of grep and awk piped through cron. When a security team wants to know “which hosts have a specific outdated OpenSSL package installed” or “did any host open a listening port on 4444 in the last hour,” the answer is a SELECT statement rather than a bespoke script written under incident-response time pressure. That is the practical case for spending an hour setting this up before you need it, not during an active incident.

What Is osquery and Why It Matters in 2026

osquery exposes operating system state — running processes, open network sockets, logged-in users, installed packages, scheduled tasks, USB devices, browser extensions — as roughly 180-plus built-in SQL tables. Instead of a bespoke agent that only reports what its authors anticipated, you query the live system the same way you would query a Postgres database. That single design decision is why osquery ended up embedded inside commercial platforms like Uptycs and why Meta, Google, and thousands of smaller security teams still run it directly.

osquery is not a SIEM and it is not a replacement for host intrusion detection. It is complementary to a tool like Wazuh: osquery answers “what is true about this machine right now, and on a schedule,” while Wazuh (or Splunk, or an ELK/OpenSearch stack) correlates that data with other telemetry, fires alerts, and gives you a dashboard. Most production setups run both — osquery for structured state, a SIEM for correlation and alerting. If you have not set up a SIEM yet, our Suricata IDS/IPS setup guide and incident response plan tutorial cover adjacent pieces of that stack.

The other reason osquery keeps showing up in 2026 security stacks is cost. Commercial EDR licensing scales with endpoint count and often locks the richest telemetry behind higher pricing tiers. osquery itself is free at any fleet size, and the operational cost is entirely your own compute and the engineering time to maintain query packs — which is exactly why it pairs well with a free or low-cost SIEM like the ELK/OpenSearch stack rather than requiring a matching commercial log platform. Teams running dozens to low hundreds of hosts frequently find this combination cheaper than a fully commercial EDR contract, though it does trade away automated response and vendor threat intelligence feeds that platforms like CrowdStrike Falcon bundle in.

Prerequisites and Versions

Before starting, confirm you have the following, cross-checked against the osquery/osquery GitHub repository. Version pinning matters here — osquery’s table schema and flag names shift between major releases, and copy-pasting a three-year-old blog post’s config will break on 5.23.1.

None of these requirements are exotic — osquery is deliberately lightweight compared to a full commercial EDR agent — but skipping the version pin in particular is the single most common reason a copy-pasted tutorial config fails on a fresh install.

  • A Linux host running Ubuntu 22.04 LTS, 24.04 LTS, or 26.04 LTS (Debian 11/12 works with the same steps) with sudo access
  • osquery 5.23.1 (released June 24, 2026) — earlier builds carry the two 2026 CVEs described above
  • At least 1 vCPU and 512MB RAM free for osqueryd; budget more if you enable file-integrity or process-auditing tables on a busy host
  • curl and gnupg installed for adding the signed APT repository
  • Optional: Docker 27.x or later if you plan to run Fleet’s server components in containers
  • Optional: Fleet (fleetdm) 4.91.0 or later if you want centralized fleet management beyond a single host
  • Optional: an existing Wazuh, Splunk, or OpenSearch endpoint if you plan to ship logs off-box in this same session

macOS and Windows are both supported osquery targets — the vendor ships signed PKG and MSI installers — but this walkthrough focuses on Linux, since that is where most fleets start. Where a step diverges meaningfully on macOS or Windows, it is called out inline.

One version note worth internalizing before you start: osquery’s schema and flag names have shifted enough between major versions that a config written for osquery 4.x will not always drop in cleanly on 5.23.1. If you are migrating an older deployment rather than starting fresh, read the release notes for the major versions in between rather than assuming a straight upgrade path — a small number of flags were renamed or deprecated along the way, and a config referencing a removed flag will prevent osqueryd from starting rather than simply ignoring the unknown option.

Step 1: Update the Host and Check Existing Agents

Patch the base OS first. osquery’s install script and several kernel-level tables (particularly the eBPF-backed process auditing tables) are sensitive to kernel version mismatches, so starting from a current kernel avoids a category of confusing failures later.

sudo apt update && sudo apt upgrade -y
uname -r
# check nothing else is already bound to /etc/osquery or the osqueryd service name
systemctl list-units --type=service | grep -i osquery

If a previous, older osquery install is present, remove it cleanly before continuing rather than installing over it — mixed-version installs are one of the most common causes of “the daemon starts but no logs appear” support threads.

sudo systemctl stop osqueryd 2>/dev/null
sudo apt remove --purge osquery -y 2>/dev/null
sudo rm -rf /var/osquery /etc/osquery

Step 2: Add the Official osquery APT Repository

Add the signed official osquery repository rather than downloading a standalone .deb from a third-party mirror. The keyring-based method below replaces the older apt-key approach, which is deprecated on current Ubuntu and Debian releases.

curl -fsSL https://pkg.osquery.io/deb/pubkey.gpg 
  | sudo gpg --dearmor -o /usr/share/keyrings/osquery-archive-keyring.gpg

ARCH=$(dpkg --print-architecture)
echo "deb [arch=${ARCH} signed-by=/usr/share/keyrings/osquery-archive-keyring.gpg] https://pkg.osquery.io/deb deb main" 
  | sudo tee /etc/apt/sources.list.d/osquery.list

sudo apt update

Run apt-cache policy osquery before installing to confirm the repo resolved and is offering 5.23.1 or later. If apt still shows an older cached version, clear /var/lib/apt/lists and rerun apt update.

Step 3: Install osquery and Verify the Version

sudo apt install osquery

osqueryi --version
# Expected: osqueryi version 5.23.1

The install drops two binaries you will use constantly: osqueryi, an interactive SQL shell for ad hoc investigation, and osqueryd, the background daemon that runs scheduled queries and writes logs. A common early mistake is treating them as interchangeable — osqueryi does not persist a configuration or run on a schedule; it exits and forgets everything when you close it. Production monitoring runs through osqueryd as a service, which the next steps set up.

Step 4: Explore the Schema With osqueryi

Before writing a config, spend five minutes in the interactive shell to confirm the tables you plan to rely on actually exist and return sane data on this specific host and kernel.

osqueryi
osquery> .tables
osquery> .schema processes
osquery> SELECT pid, name, path, cmdline, uid FROM processes LIMIT 5;
osquery> SELECT pid, address, port, protocol FROM listening_ports;
osquery> .quit

If .tables returns a short list missing tables you expected — usb_devices or chrome_extensions, for instance — that usually means the daemon is running without sufficient privileges, or the table is platform-gated (some tables are Linux-only, some macOS-only). Table availability is not uniform across operating systems, and assuming parity is a frequent source of “why is this query empty” confusion when scripts get copied between a Linux fleet and a handful of Mac laptops.

Step 5: Write Your osquery.conf

osquery’s real configuration lives in /etc/osquery/osquery.conf on Linux (macOS uses /var/osquery/osquery.conf; Windows uses C:Program Filesosqueryosquery.conf on current installs). This file defines your scheduled query pack — what runs, how often, and where results go.

sudo mkdir -p /etc/osquery
sudo tee /etc/osquery/osquery.conf > /dev/null <<'EOF'
{
  "options": {
    "config_plugin": "filesystem",
    "logger_plugin": "filesystem",
    "logger_path": "/var/log/osquery",
    "disable_logging": "false",
    "log_result_events": "true",
    "schedule_splay_percent": "10"
  },
  "schedule": {
    "listening_ports": {
      "query": "SELECT pid, address, port, protocol FROM listening_ports;",
      "interval": 300
    },
    "processes_snapshot": {
      "query": "SELECT pid, name, path, cmdline, uid FROM processes;",
      "interval": 600,
      "snapshot": true
    },
    "logged_in_users": {
      "query": "SELECT user, tty, host, time FROM logged_in_users;",
      "interval": 300
    },
    "crontab_watch": {
      "query": "SELECT * FROM crontab;",
      "interval": 3600
    },
    "usb_devices": {
      "query": "SELECT * FROM usb_devices;",
      "interval": 3600
    }
  }
}
EOF

The schedule_splay_percent option is worth keeping — it randomizes query start times slightly across a fleet so a thousand hosts do not all hammer a shared table (or a shared log collector) at the exact same second. On a single lab host it does nothing; on a real fleet it is one of the cheapest performance wins available.

Step 6: Configure osquery.flags

The flags file at /etc/osquery/osquery.flags controls runtime behavior that is not part of the query schedule itself — where the config lives, whether to run as a daemon, and log verbosity.

sudo tee /etc/osquery/osquery.flags > /dev/null <<'EOF'
--config_path=/etc/osquery/osquery.conf
--pidfile=/var/run/osqueryd.pidfile
--database_path=/var/osquery/osquery.db
--logger_path=/var/log/osquery
--disable_watchdog=false
--utc
EOF

sudo mkdir -p /var/osquery /var/log/osquery

Leave --disable_watchdog set to false unless you have a specific reason to change it. The watchdog process monitors osqueryd’s own memory and CPU usage and restarts it if a misbehaving extension or a runaway query starts eating resources — disabling it trades a safety net for a small amount of overhead you will rarely notice on modern hardware.

Step 7: Enable and Start osqueryd via systemd

sudo systemctl enable osqueryd
sudo systemctl start osqueryd
sudo systemctl status osqueryd

A healthy status output shows active (running) with no restart loop. If it flaps between starting and stopping, check journalctl immediately rather than restarting blindly — the log almost always names the exact config key or file permission that is wrong.

sudo journalctl -u osqueryd -n 50 --no-pager

Step 8: Confirm Logs Are Writing

osquery writes two categories of JSON log by default: a results log for query output and a status log for the daemon’s own operational events. With logger_plugin set to filesystem, both land under /var/log/osquery.

ls -la /var/log/osquery/
tail -f /var/log/osquery/osqueryd.results.log | python3 -m json.tool 2>/dev/null

# quick sanity check: any listening ports logged in the last query cycle?
grep listening_ports /var/log/osquery/osqueryd.results.log | tail -1

If the results log exists but stays empty after two full scheduling intervals, the daemon is running but the schedule in osquery.conf is not being read — usually a JSON syntax error that osqueryd silently ignores in favor of an empty default schedule. Validate the file with python3 -m json.tool /etc/osquery/osquery.conf before assuming anything deeper is wrong.

Step 9: Build a Security-Focused Query Pack

The schedule above is a minimal starting point. A real security monitoring pack adds queries for persistence mechanisms, credential exposure, and lateral-movement indicators. Here is a practical set to layer in once the basic pipeline is confirmed working.

-- New process starts joined with the parent, useful for catching
-- unexpected child processes spawned from web server or cron users
SELECT p.pid, p.name, p.path, p.cmdline, p.uid, pp.name AS parent_name
FROM processes p
JOIN processes pp ON p.parent = pp.pid;

-- Cron persistence
SELECT * FROM crontab;
SELECT * FROM crontab_events;

-- Newly connected USB storage
SELECT * FROM usb_devices;

-- Browser extensions across common browsers
SELECT * FROM chrome_extensions;
SELECT * FROM firefox_addons;

-- SUID/SGID binaries outside expected system paths (a classic
-- privilege-escalation persistence check)
SELECT path, uid, gid, mode
FROM file
WHERE path LIKE '/usr/local/%' AND (mode LIKE '%4___' OR mode LIKE '%2___');

Add each query to the schedule block in osquery.conf with an interval matched to how noisy and expensive it is. Process-join queries and file-system scans are heavier than a simple logged_in_users check — schedule them every 15 to 30 minutes rather than every 60 seconds, or you will find osqueryd itself becoming the top CPU consumer on the host, which defeats the purpose of a lightweight monitoring agent.

Resist the temptation to enable every table you find interesting on day one. A query pack with forty scheduled queries running every minute against a production database server will show up in that server’s own performance graphs, and it is the single fastest way to get a monitoring rollout blocked by the infrastructure team. Start with the six to eight queries above, confirm the pipeline end to end, and add tables incrementally as specific detection needs come up — file-integrity monitoring on a handful of sensitive paths, for instance, or a table watching for new sudoers entries, once the basics are proven stable.

Step 10: Ship Logs to a SIEM

A single host’s JSON logs are useful for testing; a fleet needs those logs centralized. The standard patterns, in rough order of popularity:

  • osquery to Wazuh: the Wazuh agent can tail osquery’s results log directly and forward parsed events into the Wazuh manager for correlation and alerting
  • osquery to ELK/OpenSearch: ship the JSON results log with Filebeat or Fluent Bit, index it, and build Kibana/OpenSearch Dashboards visualizations on top
  • osquery to Splunk: forward via the Splunk Universal Forwarder or push directly to Splunk’s HTTP Event Collector (HEC)

A minimal Filebeat config pointed at the results log looks like this:

filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/log/osquery/osqueryd.results.log
    json.keys_under_root: true
    json.add_error_key: true

output.elasticsearch:
  hosts: ["https://your-opensearch-host:9200"]
  index: "osquery-%{+yyyy.MM.dd}"

If you already run Wazuh, our incident response plan tutorial covers how osquery-based fleet inventory fits into a broader detection workflow, and the log destinations pattern above mirrors what Fleet’s own server does when it centralizes query results, described next.

Whichever destination you pick, ship both the results log and the status log, not just the former. The status log carries osqueryd’s own operational messages — schema load failures, watchdog restarts, table errors on specific hosts — and it is frequently the only signal that a subset of your fleet has silently stopped reporting useful data while still showing as “running” at the process level. A dashboard built only on results-log volume can mask a host that is alive but broken.

Step 11: Deploy Fleet for Centralized Management

Running osquery.conf by hand on every host does not scale past a handful of machines. Fleet (fleetdm), currently at version 4.91.0, is the most widely used open-source fleet manager for osquery — see fleetdm.com for current release notes and documentation: it enrolls hosts over TLS, distributes query packs from a central server, runs live ad hoc queries across the whole fleet, and forwards results to configurable log destinations. Kolide Fleet is the original lineage Fleet forked from, and Uptycs is a commercial platform that also builds on osquery telemetry if you want a vendor-managed option instead.

# Fleet server via Docker Compose (quickest path for a lab or pilot)
curl -L https://raw.githubusercontent.com/fleetdm/fleet/main/docker-compose.yml 
  -o docker-compose.yml
docker compose up -d

# Once the server is reachable, enroll a host with its enroll secret
sudo fleetctl package --type=deb 
  --fleet-url=https://fleet.example.com 
  --enroll-secret=YOUR_ENROLL_SECRET 
  --output=fleet-osquery.deb
sudo dpkg -i fleet-osquery.deb

Enrollment failures at this stage are almost always TLS-related: a self-signed certificate the host does not trust, a mismatched Fleet URL (http vs https, wrong port), or an enroll secret that was rotated on the server after the package was built. Regenerate the enrollment package after any secret rotation rather than trying to patch it on already-deployed hosts.

Fleet’s real value over a hand-managed config shows up the first time you need an answer across the whole fleet in minutes rather than hours. Its live query feature lets you write a single SQL statement in the web UI or via fleetctl query and have it execute against every enrolled host, streaming results back as they arrive. During an active incident — “which hosts have this specific file hash on disk right now” — that beats waiting for the next scheduled config push to pick up a one-off query, which is the workflow you would otherwise be stuck with running osquery standalone.

# Run an ad hoc query across the whole fleet from the CLI
fleetctl query --hosts=all 
  --query="SELECT pid, name, path FROM processes WHERE name = 'suspicious_binary';"

Step 12: Lock Down Permissions and the Service Account

osqueryd needs elevated privileges to read kernel-level tables like processes and open file descriptors across all users, which means it typically runs as root. That is a legitimate design tradeoff, not an oversight, but it raises the bar on protecting the config and flags files themselves — anyone who can write to /etc/osquery/osquery.conf can redirect what a root-privileged daemon executes on schedule.

sudo chown root:root /etc/osquery/osquery.conf /etc/osquery/osquery.flags
sudo chmod 644 /etc/osquery/osquery.conf
sudo chmod 644 /etc/osquery/osquery.flags
sudo chown -R root:root /var/log/osquery
sudo chmod 750 /var/log/osquery

If you are on a host with SELinux or AppArmor enforcing, confirm osqueryd has an appropriate policy rather than disabling enforcement to make it start — a permissive workaround here quietly removes protection that has nothing to do with osquery itself.

Step 13: Validate With a Simulated Detection

Before calling the deployment done, prove the whole pipeline actually fires end to end. Spin up a throwaway listener and confirm it shows up in your logs (and your SIEM, if wired up) within one scheduling interval.

# On the monitored host, open a test listener
python3 -m http.server 8899 &

# Wait for the next scheduled listening_ports interval, then check
grep '"port":8899' /var/log/osquery/osqueryd.results.log

# Clean up
kill %1

If that port shows up in the log within the expected interval, the agent, the schedule, and the logging pipeline are all confirmed working. If it does not, work backward through steps 8 through 10 rather than re-installing osquery from scratch — a missing detection at this stage is almost always a schedule or log-shipping issue, not an installation issue.

Complete Working Project

Putting the pieces above together, a minimal but production-usable osquery deployment consists of five files: the APT source, osquery.conf with a security query schedule, osquery.flags, a systemd override for hardening, and a log-shipping config. The table below maps each to the step that created it.

File Path Purpose Created In
APT repo source /etc/apt/sources.list.d/osquery.list Pulls signed 5.23.1 packages Step 2
Query schedule /etc/osquery/osquery.conf Defines what runs and how often Step 5, 9
Runtime flags /etc/osquery/osquery.flags Daemon behavior, paths, watchdog Step 6
systemd unit osqueryd.service (packaged) Runs the daemon persistently Step 7
Log shipper config filebeat.yml or equivalent Forwards JSON logs to SIEM Step 10
Fleet enrollment package fleet-osquery.deb Centralized management at scale Step 11

That set of files, checked into a configuration-management repo (Ansible, Salt, or even a plain shell script run through cloud-init), is what most teams actually deploy across a fleet — the interactive exploration from Step 4 is a one-time discovery exercise, not something repeated per host.

osquery vs Wazuh vs OSSEC: Where Each One Fits

A recurring question once the install is working is whether osquery replaces a host intrusion detection tool like Wazuh or its ancestor OSSEC. It does not — the three occupy different layers, and most serious deployments run osquery alongside one of the other two rather than choosing between them.

Capability osquery Wazuh OSSEC
Primary model SQL queries against live OS state Agent-based HIDS + SIEM Agent-based HIDS (Wazuh’s origin project)
File integrity monitoring Via scheduled file table queries Built-in FIM module Built-in FIM module
Alerting/correlation None natively — needs a SIEM Built-in rules engine and dashboard Built-in rules engine, no dashboard
Ad hoc fleet queries Yes, via osqueryi/Fleet live queries Limited No
Typical role Endpoint visibility layer Detection, alerting, compliance Legacy HIDS, largely superseded by Wazuh

Wazuh actually ships built-in integrations for consuming osquery output directly, which makes the pairing straightforward: osquery answers structured questions about the endpoint, Wazuh correlates that with network and log data and raises the alert. If you have not deployed Wazuh yet, our vulnerability scanning tutorial and the CIS Benchmarks hardening guide cover complementary hardening steps worth doing before you layer on detection tooling.

Common Pitfalls

These five mistakes account for the majority of failed or stalled osquery deployments seen in the wild. Most of them share a root cause: osquery is deceptively simple to get a first query working in, which encourages skipping the operational planning that a root-privileged, fleet-wide agent actually deserves.

  • Treating osqueryi as the production agent. The interactive shell is for investigation only. It does not run a schedule, does not persist configuration, and exits the moment you close the terminal. Production monitoring runs exclusively through osqueryd as a systemd service.
  • Running an unprivileged install. osqueryd needs root to read kernel-level tables across every user’s processes and files. Installing it under a restricted service account without adjusting expectations leads to silently empty results on tables like processes and file, which looks like a bug but is actually a permissions gap.
  • Scheduling expensive queries too frequently. File-system scans and process-join queries are heavier than simple state checks. Running them every 60 seconds on a busy production host turns the monitoring agent into a measurable load source, which is exactly the outcome a lightweight agent is supposed to avoid.
  • Skipping JSON validation on osquery.conf. A single trailing comma or unescaped quote in the config produces a daemon that starts cleanly but silently falls back to an empty schedule — no crash, no obvious error, just no logs.
  • Ignoring platform-specific table availability. Not every table exists on every OS. Copying a Linux-tuned query pack onto macOS or Windows hosts without checking table availability first produces confusing gaps in coverage that look like detection failures but are actually schema mismatches.

Troubleshooting

Work through these in order when something is not behaving as expected. Most osquery issues trace back to one of three root causes — a permissions gap, a malformed config, or a mismatch between what a query expects and what the host’s platform actually supports — so ruling those out systematically saves more time than guessing.

  • osqueryd won’t start / restart loop: run journalctl -u osqueryd -n 50 --no-pager first — the exact failing flag or file is almost always named in the last few lines.
  • “database is locked” errors: another osqueryd process or a leftover PID file is holding /var/osquery/osquery.db. Stop the service, remove the stale osqueryd.pidfile, and restart.
  • Results log stays empty after two intervals: validate osquery.conf with python3 -m json.tool /etc/osquery/osquery.conf — a silent JSON error is the most common cause.
  • High CPU from osqueryd: check which scheduled query is expensive with SELECT name, wall_time, user_time FROM osquery_schedule ORDER BY wall_time DESC; in osqueryi, then raise that query’s interval.
  • Table returns zero rows unexpectedly: confirm the table exists on this platform with .tables in osqueryi, and confirm osqueryd is running as root, not a restricted user.
  • Fleet enrollment fails with a TLS error: check the Fleet URL scheme and port match exactly what the server is serving, and that the host trusts the certificate — self-signed certs need to be distributed to enrolling hosts explicitly.
  • Fleet enrollment fails with “invalid enroll secret”: the secret was rotated on the server after the enrollment package was built. Regenerate the package with the current secret rather than editing the deployed one.
  • Log shipper (Filebeat/Fluent Bit) shows no new events: confirm the shipper’s file path matches logger_path in osquery.flags exactly, including trailing slashes, and that the shipper’s service account can read /var/log/osquery.
  • Windows-specific: unexpected privilege escalation behavior on tables like processes or authenticode: confirm the host is on 5.23.1 or later — CVE-2026-54000 and CVE-2026-54001 both affect earlier Windows builds and are fixed in this release.

Advanced Tips

Once the base deployment is stable, a few refinements make it genuinely useful for a security team rather than just a curiosity.

Use snapshot queries (as shown for processes_snapshot in Step 5) rather than differential logging for tables where you want the full current state every cycle, not just what changed since the last run — this matters for inventory-style queries where “nothing changed” is itself useful information, not noise to suppress. Reserve differential logging (osquery’s default mode) for tables like listening_ports where you specifically care about new or removed entries.

Extend osquery with custom tables via its extensions API when a built-in table does not cover something specific to your environment — a custom application’s health endpoint, a proprietary agent’s status file, or an internal inventory system. Extensions run as separate processes communicating over Thrift, so a buggy extension cannot crash the core daemon, which keeps this a low-risk way to extend coverage.

If you are managing more than roughly 50 hosts, move query pack management into Fleet or an equivalent rather than distributing osquery.conf by hand through configuration management. Fleet’s live query feature — the ability to run an ad hoc SQL query across the entire fleet in real time — is disproportionately valuable during an actual incident, when you need an answer like “which hosts have this file hash present” in minutes, not after the next scheduled config push.

Track the daemon’s own resource usage over time rather than assuming it stays constant. The osquery_schedule and osquery_info virtual tables expose osqueryd’s per-query timing and overall process stats, which is worth querying itself on a longer interval and shipping to your SIEM alongside the security data. That gives you a historical record of which queries got more expensive as the environment changed — a table that was cheap on a host with a few dozen processes can get noticeably slower on a host running a few thousand containers.

Finally, treat your query pack as code. Store osquery.conf and any Fleet-managed query packs in version control, review changes the same way you would review application code, and roll out schedule changes gradually rather than pushing a new query to every host simultaneously. A single expensive query pushed fleet-wide at once has taken down monitoring pipelines by overwhelming the log destination, which is a self-inflicted outage that a staged rollout avoids entirely.

Example Output

A healthy results log entry for the listening_ports query looks like this once the pipeline is fully working:

{
  "name": "listening_ports",
  "hostIdentifier": "web-app-01",
  "calendarTime": "Thu Sep 10 14:02:11 2026 UTC",
  "unixTime": 1757512931,
  "columns": {
    "pid": "1842",
    "address": "0.0.0.0",
    "port": "443",
    "protocol": "6"
  },
  "action": "added"
}

The "action": "added" field is what differential logging produces — it tells you this port was not present in the previous scheduled run and just showed up. That single field is frequently the fastest way to spot an unauthorized service binding to a port it should not be touching.

Snapshot-mode queries, like the processes example from Step 5, look slightly different — there is no “added” or “removed” action because every row is logged fresh on each interval:

{
  "name": "processes_snapshot",
  "hostIdentifier": "web-app-01",
  "calendarTime": "Thu Sep 10 14:10:00 2026 UTC",
  "unixTime": 1757513400,
  "action": "snapshot",
  "snapshot": [
    {"pid": "1", "name": "systemd", "path": "/usr/lib/systemd/systemd", "uid": "0"},
    {"pid": "1842", "name": "nginx", "path": "/usr/sbin/nginx", "uid": "33"}
  ]
}

Use differential logging for anything where a state change is the interesting event, and snapshot logging for anything where you want a periodic full inventory regardless of whether it changed — mixing the two modes correctly across your query pack is what keeps log volume manageable without losing the signal you actually care about.

Licensing, Governance, and Where the Project Is Headed

osquery is Apache 2.0 licensed and governed by the osquery Foundation under the Linux Foundation, which means no single company controls its roadmap or can relicense it out from under existing deployments. That governance structure is part of why it has become a common building block inside both free tooling (Fleet, Kolide’s original open-source server) and commercial platforms (Uptycs) rather than staying a single-vendor product. For a team evaluating whether to build on it, that neutrality is a meaningful factor — you are not locked into one vendor’s roadmap or pricing decisions by adopting the underlying agent.

The June 2026 release cadence that produced 5.23.1 also reflects an active maintenance posture: both CVEs fixed in that release were reported and patched within the same month, which is the kind of turnaround that matters when you are trusting an agent to run with root privileges across a fleet.

Frequently Asked Questions

Is osquery free?

Yes. osquery itself is Apache 2.0 licensed and free to use. Fleet’s core open-source edition is also free; Fleet Premium and commercial platforms like Uptycs that build on osquery charge for additional management, compliance, and support features.

Does osquery replace antivirus or EDR?

No. osquery provides structured visibility into system state through scheduled and ad hoc queries; it does not do signature-based malware detection or automated response. It is commonly deployed alongside an EDR platform rather than instead of one — see our CrowdStrike Falcon vs Cortex XDR vs Defender XDR comparison for how those platforms differ from an endpoint-visibility tool like osquery.

What is the difference between osqueryi and osqueryd?

osqueryi is the interactive SQL shell for one-off, ad hoc investigation. osqueryd is the background daemon that runs your scheduled query pack continuously and writes logs. Production monitoring always runs through osqueryd as a systemd service, not through osqueryi.

How many tables does osquery support?

Current releases ship roughly 180 or more built-in tables covering processes, network state, users, packages, scheduled tasks, hardware, and more, with the exact count varying by platform and version. Extensions can add further custom tables beyond the built-in set.

Do I need Fleet to use osquery?

No. osquery runs standalone with a local osquery.conf on a single host, which is exactly what Steps 1 through 9 of this tutorial cover. Fleet becomes valuable once you are managing enough hosts that hand-editing configuration files per machine stops scaling, typically somewhere past a handful of servers.

Is osquery safe to run on production servers?

Yes, with the caveats covered in this tutorial’s pitfalls and troubleshooting sections: pin to the current version (5.23.1 or later, given the two CVEs fixed in that release), schedule expensive queries at reasonable intervals rather than every few seconds, and keep the watchdog enabled so a misbehaving query cannot consume unbounded resources.

Can osquery detect malware directly?

Not on its own in real time. It can surface indicators — an unexpected process, a newly opened port, a suspicious SUID binary — that a security team or SIEM rule can flag as suspicious, but it does not carry a signature database or built-in threat-detection engine the way dedicated antivirus or EDR products do.

What SIEM works best with osquery?

There is no single correct answer — Wazuh has native osquery integration built in, while Splunk and the ELK/OpenSearch stack both consume osquery’s JSON logs equally well through a forwarder like Filebeat or Fluent Bit. The right choice depends on what your team already operates rather than any technical limitation in osquery’s output format.

Related Coverage

Source: Tech Insider