During one of our internal cost reviews, we realized something painful:Our planned TeamViewer subscription was about to cost us around 500$~ per month because our team was growing beyond the 30 allowed users in the TeamViewer corporate plan.
For a tool we mostly use to jump into edge devices, troubleshoot, and help colleagues, that felt like a lot, since up until the 30-user mark we were paying up to $ 150 per month.
So, we went looking for an alternative and landed on RustDesk – an open-source remote desktop tool that you self-host, with a Pro server for user/device management, SSO, and other enterprise features.
This article is the story of that migration: why we chose RustDesk, where it shines, and all the sharp edges we hit along the way.
What you’ll learn from this article
- What RustDesk actually is and where it’s a good fit.
- Real-world cost comparison: TeamViewer vs self-hosted RustDesk (30 users / 300 devices).
- Operational gotchas you should know before going all-in:
- Device IDs and naming
- Device sprawl and license limits
- Cleanup without bulk delete
- SSH tunneling and idle disconnects
- Firewall and networking
- SSO and access control
- What RustDesk does really well once you tame it.
- A short checklist to decide if RustDesk is right for your team.
What is RustDesk, in one paragraph?
RustDesk is an open-source remote desktop & remote support tool, positioned explicitly as an alternative to TeamViewer, AnyDesk, and similar SaaS tools.
Key things to know:
- Self-hosted server (RustDesk Server OSS / RustDesk Server Pro)
- End-to-end encrypted connections
- Cross-platform clients: Windows, macOS, Linux, Android, iOS
- Pro features (with a paid license): web console, device management, address book, custom client builder, OIDC/LDAP SSO, etc.
So conceptually, instead of paying a vendor to host your remote desktop infrastructure, you become the vendor.
The money: TeamViewer quote vs RustDesk reality
The use case was a growing team just surpassing the 30-user mark, needing around 300 managed devices.
Since my client is a growing company, I recommended choosing a solution that allows them to scale the number of users and devices without incurring high additional costs. Read here about RustDesk pricing.
On top of that, you need infrastructure. RustDesk’s own docs say that a very small VPS (1 vCPU / 1–2 GB) is enough for hundreds–thousands of connections, and their public ID server runs 1M+ endpoints on 2 vCPU / 4 GB.
In our case, infra is basically:
- EC2 instance
- NLB in front of it
So we ended up with roughly:
TeamViewer: allows up to 30 users for 150$/month and was going to cost ~500$/month when growing the team – this also doesn’t account for extra concurrent connections.
RustDesk: around $60/month license + relatively cheap EC2 + NLB
Financially, RustDesk wins easily.
The real question isn’t cost.
It’s how much operational effort you’re willing to take on.
The trade-off: when you self-host, you become the vendor
In practice, moving to RustDesk meant trading license cost for operational ownership. Instead of paying for a fully managed, polished solution, we took responsibility for hosting, hardening, and integrating the tool ourselves and knowing there isn’t a strong support system with clear SLAs.
Concretely, that meant:
- Running and maintaining RustDesk Server Pro (Docker / VM).
- Keeping ports and firewall rules in shape (including all the 2111x ports).
- Writing our own tooling around device onboarding and cleanup.
- Accepting that documentation is fragmented:
- Part of the official docs site
- Part hidden in GitHub issues/discussions (for things like license behavior, device limits, edge cases, etc.)
Now let’s walk through the main operational lessons we learned.
Lesson 1 – Device identity & naming are non-optional
Out of the box, devices register themselves to your RustDesk server with:
- An auto-generated numeric ID
- A device name based on hostname
- Some hardware / OS metadata
That sounds great — until you start re-installing clients and running scripts.
What we actually needed
We quickly learned we needed a repeatable way to ensure:
- Each device has a stable, unique identity
- Names are human-readable and consistent
- If the device was registered beforehand override it
We ended up using Ansible to manage new and existing devices:
- Push RustDesk client & config
- Generate or enforce a unique ID per machine based on the hardware of the machine
- Set a device name that matches our internal naming convention
If you don’t do this, you’ll get:
- Duplicate registrations
- Non meaningful names
- A lot of “is this the right machine?” moments
Takeaway:
If you don’t automate it, you’ll drown in chaos.
Device sprawl can quietly eat your license
RustDesk does not prevent the same physical device from registering multiple times.
So when we iterated on our install script a few times, each run:
- Re-registered the device
- Consumed another “managed device” slot on our license
Result: we hit our device limit much earlier than expected and had to clean up aggressively.
Mitigations that helped us:
- Be very careful when testing installation scripts on production devices.
- Make your script idempotent:
- If RustDesk is already installed and configured, don’t re-register blindly
Instead of relying on the ID generated by RustDesk we generate a hardware-based unique identifier for each device, ensuring it remains consistent—even if the device is reformatted—while still being globally unique.
#!/bin/bash
get_device_id() {
local hw_id=""
if [[ "$OSTYPE" == "darwin"* ]]; then
hw_id=$(ioreg -d2 -c IOPlatformExpertDevice | awk -F\" '/IOPlatformUUID/{print $(NF-1)}')
elif [ -f /proc/device-tree/serial-number ]; then
hw_id=$(tr -d '\0' </proc/device-tree/serial-number)
elif [ -r /sys/class/dmi/id/product_uuid ]; then
hw_id=$(cat /sys/class/dmi/id/product_uuid)
fi
# If nothing valid found, fallback to machine-id
if [ -z "$hw_id" ]; then
hw_id=$(cat /etc/machine-id 2>/dev/null)
fi
# Normalize and hash for uniformity
local hash=$(echo -n "$hw_id" | sha1sum | cut -c1-14)
echo "zm${hash}"
}
DEVICE_ID=$(get_device_id)
echo $DEVICE_ID
Then using this ID we can make sure that it is aligned with what is in RustDesk, validating the ID makes sure the device was not duplicated.
echo "📝 Stage 1/6: Setting RustDesk ID on device..."
if ! sudo /usr/bin/zimark-rustdesk --set-id "$DEVICE_ID"; then
echo "❌ Failed to set RustDesk ID"
exit 1
fi
echo "✅ RustDesk ID set successfully"
echo ""
# Add delay for ID to be written
sleep 2
echo ""
# Stage 2: Validate ID
echo "🔍 Stage 2/6: Validating RustDesk ID..."
CURRENT_ID=$(sudo /usr/bin/zimark-rustdesk --get-id 2>/dev/null || echo "")
if [ "$CURRENT_ID" != "$DEVICE_ID" ]; then
echo "❌ ID mismatch! Expected: $DEVICE_ID, Got: $CURRENT_ID"
exit 1
fi
echo "✅ RustDesk ID confirmed: ${DEVICE_ID:0:14}..."
echo ""
Lesson 2 – Cleanup is painful
Once we realized we’d polluted our device list with duplicates, we assumed we could fix it in a couple of clicks.
Not quite.
UI limitations
- Basic things like IP display are quirky:
- Long IPs appear truncated (e.g. 31.154.4…) without a way to expand the column.
- If you copy the value, you do get the full IP — but you can’t actually see it in the UI, which makes verification annoying.
So we turned to the API.
API limitations
The device API returns data similar to:
{
"guid": "019a589a-71d3-7a61-a049-4ae90762f2f4",
"id": "1509660827",
"info": {
"ip": "::ffff:31.154.4.200",
"device_name": "my-device",
"version": "1.4.0",
"cpu": "Cortex-A78AE, 1.39GHz, 4/4 cores",
"memory": "7.44GB",
"os": "ubuntu / Linux 22.04 Ubuntu",
"username": "user"
},
"status": 1,
"device_group_name": "devices",
"strategy_name": "-",
"last_online": "2025-11-10T14:32:56.403757339"
}
Useful, but with a big gap:
- You don’t get a straightforward “online right now” flag
- You get status and last_online – which is helpful but not enough to safely automate deletions
So any cleanup logic you write is, by default, risky:
- If you delete “devices not seen for X days”, you may still nuke something that will come back next week.
- If the client app is still installed on that machine, it may re-register after your cleanup — which is good for re-used devices, but bad for machines you wanted gone.
We ended up combining:
- Manual cleanup in the UI for sensitive devices
- Script-based deletion for development devices that were duplicated.
Takeaway:
Budget time to build tooling around device lifecycle. RustDesk gives you the primitives, not a polished lifecycle management story.
Lesson 3 – SSH tunneling is great… until idle kills your session
RustDesk supports TCP tunneling, so you can forward ports (like SSH) through the RustDesk connection instead of exposing them directly.
Out of the box, we noticed something annoying:
- If you open an SSH tunnel through RustDesk and stay idle, the tunnel gets killed after 30 seconds of inactivity.
How we stabilized it
To keep SSH sessions alive over RustDesk tunnels, we:
- Set client-side keep-alive (e.g. ServerAliveInterval / ServerAliveCountMax)
- Or server-side keep-alive (ClientAliveInterval / ClientAliveCountMax) on the SSH server
- name: Debug SSH KeepAlive settings
debug:
msg: "KeepAlive settings"
- name: Remove any existing keepalive settings
lineinfile:
path: /etc/ssh/sshd_config
regexp: '{{ item }}'
state: absent
loop:
- '^#?TCPKeepAlive'
- '^#?ClientAliveInterval'
- '^#?ClientAliveCountMax'
- name: Add SSH keepalive settings block
blockinfile:
path: /etc/ssh/sshd_config
marker: "# {mark} ANSIBLE MANAGED - SSH KEEPALIVE"
block: |
TCPKeepAlive yes
ClientAliveInterval 15
ClientAliveCountMax 40
backup: yes
notify: restart sshd
The idea is the same as keeping SSH sessions alive through stateful firewalls:
send periodic “I’m alive” packets so the tunnel doesn’t look idle.
This still relies on having a device set up script or proper process to make sure all devices support this.
Lesson 4 – Firewalls and ports: RustDesk is not “port 443 only”
According to official docs, for a typical self-hosted setup you should expect to open:
- Core ports (server side)
- TCP 21114–21119
- UDP 21116
- With Docker examples mapping:
- hbbs (ID server): 21115, 21116 (TCP/UDP), 21118 (web client)
- hbbr (relay): 21117, 21119
If you don’t need the web client, you can skip 21118/21119, but the 21115–21117 + 21116/UDP are minimum.
On cloud providers like AWS, docs explicitly say:
open inbound UDP 21116 and TCP 21114–21119 in your security group / firewall.
In a corporate environment, that usually means:
- Talking to the network/security team
- Making sure egress rules and NAT won’t break traversal
Lesson 5 – SSO is there, but IAM is still a bit manual
On the plus side, the Basic and Customized plans include:
- OIDC (SSO)
- LDAP integration
- 2FA
- Custom client generator
- Strategy-based access control
In practice, though, our experience was:
- We could hook RustDesk up to our IdP (e.g. Azure AD) for authentication.
- But fine-grained permissions and group mappings still needed manual work:
- When a user logs in for the first time, you still manually assign them to the right groups / strategies.
So from an IAM perspective:
- It’s good enough for small teams and simple role models.
- It’s not (yet) on the level of deep, auto-synced RBAC that enterprises might be used to.
What RustDesk does really well
After all the complaining above, it’s worth emphasizing: once you put in the work, RustDesk is genuinely nice to use.
Here are the parts we really liked.
1. Custom clients & pre-configured security
RustDesk Server Pro includes a custom client generator that builds a client binary that automatically registers with your RustDesk server/relay server, supports your branding, and comes with preconfigured security settings.
Which meant we only had to ship one installer for everyone.
Given how much we care about reproducible configuration, this feature alone is a big win.
2. Performance and UX
Subjectively, connection quality is solid, especially once you create automations to install it on devices and ensure firewalls properly allow the required ports.
It allows unlimited concurrent connections, eliminating many of the extra costs other similar tools incur.
The client UI is clean; in my opinion, it feels lighter and less cluttered than some legacy remote desktop tools.
3. Data sovereignty & control
Because you self-host, all metadata, logs and connections go through your own infrastructure and not through a 3rd party.
You’re not tied to a single vendor SaaS for something as sensitive as remote desktop.
When does RustDesk make sense?
Based on my experience, RustDesk is a great fit if:
You’re willing to invest some time and development effort to integrate it.
The effort it takes is definitely worth it in the long run, once the tool is stable, it saves thousands of dollars.
Your company is growing, and you expect the number of users and devices to increase over time. RustDesk’s pricing model makes scaling users and devices much cheaper than its counterparts.
This also applies to concurrent connections. If you need to scale your concurrent connections, RustDesk offers unlimited concurrent connections, while TeamViewer and AnyDesk charge extra.
Data sovereignty and privacy are important to you. Because RustDesk is self-hosted, your remote sessions and metadata remain within your own infrastructure rather than passing through a third-party vendor.
Some things to consider if you’re thinking about moving to RustDesk:
To make that work, you do need some DevOps/SRE capacity. In practice, that means being comfortable with:
- Running and maintaining containerized services
- Managing firewalls, ports, and load balancers
- Writing small automation scripts for things like device registration and cleanup
On the other hand, you might want to stick with something like TeamViewer/AnyDesk if:
- You have a small team without a dedicated technical person to own the setup.
- You need a mature solution with strong documentation and clear support SLAs.
- You’re happy to pay more to avoid the operational overhead.
What I’d do differently next time
If I had to roll out RustDesk again from scratch, I’d:
- Define a device lifecycle upfront
- When is a device considered “stale”?
- How often do we review / clean up?
- Do we disable first, then delete?
- Automate as much as possible around:
- Unique IDs and naming
- Tagging devices and using address book features (office, environment, customer, etc.)
- Document internal RustDesk “runbook” for:
- How to onboard users
- How to onboard devices
- How to safely clean up
Once those are in place, RustDesk becomes stable – it becomes a reliable tool which has a lot of abilities.
Final thoughts
RustDesk is not a drop-in SaaS clone of TeamViewer.
It’s more like a remote-desktop platform you can shape to your needs – if you’re willing to own the operational side.
In our case, the math was simple:
- Cost: a fraction of what we’d pay for TeamViewer
- Effort: a non-trivial but manageable amount of work
- Outcome: once we stabilized device registration and workflows, RustDesk has been a fast, pleasant tool that our team likes using.
If your organization is already comfortable with Docker, cloud infrastructure, and automation tools like Ansible and Rundeck, RustDesk is absolutely worth a serious look.