Purging Stale Clients from UniFi Network Controller in Bulk

I’ve been rocking UniFi equipment at home for a long while now, I’ve been through many updates to the UniFi platform, and as a result, my client database has become rather ugly and unwieldy.

Many moons ago, UniFi published a script to purge the MongoDB database to alleviate the issue, but this has aged and since been removed from documentation. I mean, there’s a good chance this action is unsupported, but they did once recommend it.

Anyway, the name table that is referenced in this ancient script no longer exists, so running the once functional script does nothing. At least it appears to do nothing (your mileage may vary – be warned!).

In a bid to reduce my stale client list from hundreds, to merely none at all, filtered by only those connecting in the last 7 days, I ended up rolling the following script, which can be executed from the UniFi OS shell.

mongo --port 27117

After connecting to mongo, switch to the ace database.

use ace;

Then, once we’re on the ace database, we can run our queries. The code below will search the User table and return clients who have not connected within 7 days.

// Current time in seconds minus 7 days
var cutoff = Math.floor(Date.now() / 1000) - (7 * 24 * 60 * 60);

// Get all matching documents into a variable
var oldUsers = db.user.find(
  { last_seen: { $lt: cutoff } },
  { _id: 1, hostname: 1, mac: 1, ip: 1, last_seen: 1 }
).toArray();

// Optional: Print them with human-readable last_seen
oldUsers.forEach(function(doc) {
  doc.last_seen_human = new Date(doc.last_seen * 1000);
  printjson(doc);
});

You can change this timeframe to whatever period you like, for example, 30 days, the first line would read;

var cutoff = Math.floor(Date.now() / 1000) - (30 * 24 * 60 * 60);

Under the assumption we want to delete the clients that have not connected recently, you would then run the following code, which will group up all the clients returned in the result, add them to a variable, and then delete them from the User table.

// Extract IDs from the results
var idsToDelete = oldUsers.map(function(doc) { return doc._id; });

// Delete them
db.user.deleteMany({ _id: { $in: idsToDelete } });

Altogether, this reads;


// Current time in seconds minus 7 days
var cutoff = Math.floor(Date.now() / 1000) - (7 * 24 * 60 * 60);

// Get all matching documents into a variable
var oldUsers = db.user.find(
  { last_seen: { $lt: cutoff } },
  { _id: 1, hostname: 1, mac: 1, ip: 1, last_seen: 1 }
).toArray();

// Optional: Print them with human-readable last_seen
oldUsers.forEach(function(doc) {
  doc.last_seen_human = new Date(doc.last_seen * 1000);
  printjson(doc);
});

// Extract IDs from the results
var idsToDelete = oldUsers.map(function(doc) { return doc._id; });

// Delete them
db.user.deleteMany({ _id: { $in: idsToDelete } });

Pressing return and executing this will remove the stale devices. Happy days.

James avatar

10 responses to “Purging Stale Clients from UniFi Network Controller in Bulk”

  1. Matt Tracey

    Thanks for this post, it gave me a great starting place!

    We have a similar issue with various “ghost” clients appearing in our controller —they never get an IP address and seem to automatically show as offline after a short period of time, so all they do is fill up the Client Devices section with useless offline clients.

    After some trial and error we ended up with this ‘mongosh’ command that can find these specific clients, filtered by having no IP, “last_seen” value is an hour or more ago, and only matching specific network connections by also filtering on components of the name pulled from the Unifi controller’s Settings > Networks area (set in the $regex line). Hope this can help others!

    mongosh “mongodb://localhost:27117/ace” –eval ‘
    const oneHourAgo = Math.floor(Date.now() / 1000) – 3600;
    const docs = db.user.find({
    last_ip: { $in: [null, undefined] },
    last_connection_network_name: {
    $regex: /LAN|BYOD|LAN2/i
    },
    last_seen: { $lt: oneHourAgo }
    }).toArray();
    console.log(“Deleting the following clients:”);
    console.log(docs);
    db.user.deleteMany({ _id: { $in: docs.map(d => d._id) } });

    1. James

      Nice one, glad it gave you something to work with. Thanks for sharing your code!

  2. Gaston

    Thanks for this post James. It gave me hope.
    I ran the commands and everything worked as expected… for the first few minutes.Then, many of the devices that remained in the clients list started to loose their aliases and icons (they now show their factory like name, or MAC address, or some combination of that, and the system default icon).
    Have you experienced something similar? May be there is a fix for that?
    I’m running a UDM Pro Max 4.3.6 with Network 9.5.21.
    Regards.

    1. James

      I had no such issue I’m afraid. The issue I had was that the guides I found online were old and referred to tables that had changed name etc…

      It’s plausible that similar has happened again. Or that something else in your DB has these records cached. I’m guessing a trusty reboot didn’t help?

      1. Gaston

        Thx James for your response.
        I rebooted a couple of times and issue persists. I’ll restore from back up and give it another try. I’ll keep you posted. Regards.

        1. James

          Personally, I’d try to fix forward. If they’re visually in the UI, they must be in the DB somewhere. If you’re technical, have a sniff around the DB and see if you can find them and remove them?

    2. James

      Morning Gaston,

      I actually noticed this morning I have ghost clients once again, that mirrors your description. I wonder if a newer UnifiOS has brought them out of the closet.

      Either way, this is now on my to-do, to investigate.

      1. Gaston

        Hi James.
        Thanks for the update. In my case, I went ahead and restore from back up and re-run the deletion script. So far so good. None of the remaining clients have lost their aliases or icons. My previous run was right after migrating from UDM Pro to UDM Pro Max. The second run after restoring from a Pro Max native backup. May be there was something in the migration process that didn’t settle correctly and thus the issues. Fingers crossed. Thanks again for taking the time to come up with a solution to address this need for the Unifi user community!!
        Regards, Gaston.

  3. peter

    worked for me as well. I had the USBc hub with ethernet port fill 894 clients before I caught it. I used the “first _seen” field and a $gt to catch my ghosts that appeared today (in the last 20 hours) and removed them that way. Thanks for the help.

    1. James

      Hi Peter, glad you were able to utilise this as a foundation to put something together that worked for you!

Leave a Reply to peter Cancel reply

Your email address will not be published. Required fields are marked *