<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://amrosia.dev/feed_style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <tabi:metadata xmlns:tabi="https://github.com/welpo/tabi">
        <tabi:base_url>https:&#x2F;&#x2F;amrosia.dev</tabi:base_url>
        <tabi:separator>
            •
        </tabi:separator>
        <tabi:about_feeds>This is a web feed, also known as an Atom feed. Subscribe by copying the URL from the address bar into your newsreader. Visit About Feeds to learn more and get started. It&#x27;s free.</tabi:about_feeds>
        <tabi:visit_the_site>Visit website</tabi:visit_the_site>
        <tabi:recent_posts>Recent posts</tabi:recent_posts>
        <tabi:last_updated_on>Updated on $DATE</tabi:last_updated_on>
        <tabi:default_theme>dark</tabi:default_theme>
        <tabi:post_listing_date>date</tabi:post_listing_date>
        <tabi:current_section>android</tabi:current_section>
    </tabi:metadata><link rel="extra-stylesheet" href="https://amrosia.dev/skins/teal.css?h=bd19e558a52d678a50de" /><title>Segfault diaries - android</title>
        <subtitle>Welcome to my blog!</subtitle>
    <link href="https://amrosia.dev/tags/android/atom.xml" rel="self" type="application/atom+xml"/>
    <link href="https://amrosia.dev/tags/android/" rel="alternate" type="text/html"/>
    <generator uri="https://www.getzola.org/">Zola</generator><updated>2026-10-24T00:00:00+00:00</updated><id>https://amrosia.dev/tags/android/atom.xml</id><entry xml:lang="en">
        <title>A Proxmox Lab That Runs Without Your Laptop</title>
        <published>2026-10-24T00:00:00+00:00</published>
        <updated>2026-10-24T00:00:00+00:00</updated>
        <author>
            <name>amrosia</name>
        </author>
        <link rel="alternate" href="https://amrosia.dev/blog/four-remote-workloads-on-one-proxmox-host/" type="text/html"/>
        <id>https://amrosia.dev/blog/four-remote-workloads-on-one-proxmox-host/</id>
        
            <content type="html">&lt;p&gt;Run an Android Emulator, Windows desktop, Kali lab, and Herdr agent on Proxmox instead of squeezing them onto a laptop. The host supplies the CPU, RAM, and storage; the guests keep running when the laptop disconnects or shuts down.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;proxmox&#x2F;four-workloads.gif&quot; alt=&quot;Animated diagram: a workstation connects through Tailscale to four workloads on a Proxmox host: Android, Windows, Kali, and Herdr.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-run-these-remotely&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#why-run-these-remotely&quot; aria-label=&quot;Anchor link for: why-run-these-remotely&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Why run these remotely?&lt;&#x2F;h2&gt;
&lt;p&gt;The laptop becomes a client, not the machine doing the work. A Proxmox host can allocate more cores, RAM, and disk to a workload than the laptop has available, and can run several isolated workloads at once. The host must remain powered on, but the laptop need not: close it, disconnect from the network, or switch devices without shutting down the guests.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Android:&lt;&#x2F;strong&gt; Keep a hardware-accelerated Google AVD running without consuming the laptop’s RAM or requiring a local emulator boot for each test.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Windows:&lt;&#x2F;strong&gt; Reach a full Windows desktop from a lightweight client while its VM remains available for long-running tasks.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Kali:&lt;&#x2F;strong&gt; Keep tools and a lab network in a separate VM; reconnect to the same environment later.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Herdr:&lt;&#x2F;strong&gt; Agents and terminal panes keep running in the VM even while the laptop is off. Reconnect to the same session later.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;prerequisites&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#prerequisites&quot; aria-label=&quot;Anchor link for: prerequisites&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Prerequisites&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Proxmox VE 9 on x86-64 with AMD-V or VT-x enabled. Check &lt;code&gt;test -r &#x2F;dev&#x2F;kvm &amp;amp;&amp;amp; ls -l &#x2F;dev&#x2F;kvm&lt;&#x2F;code&gt; on the host.&lt;&#x2F;li&gt;
&lt;li&gt;Enough RAM and thin-pool space for &lt;strong&gt;all&lt;&#x2F;strong&gt; running guests. The example uses 8 GiB for Android, 4 GiB each for Windows and Kali, and 2 GiB for Herdr, before host overhead. Thin-provisioned disks can still fill their pool.&lt;&#x2F;li&gt;
&lt;li&gt;A bridge such as &lt;code&gt;vmbr0&lt;&#x2F;code&gt;, working DHCP&#x2F;DNS, an SSH key for Proxmox, and Tailscale on Proxmox and the workstation. Do not expose the Proxmox UI, ADB, RDP, or Herdr to the public Internet.&lt;&#x2F;li&gt;
&lt;li&gt;Official installation media and a Windows Pro&#x2F;Enterprise&#x2F;Education license. Windows Home cannot host incoming RDP.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;On the workstation, define a key-authenticated SSH alias in &lt;code&gt;~&#x2F;.ssh&#x2F;config&lt;&#x2F;code&gt; (replace the address and key path):&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;sshconfig&quot;&gt;Host pve
    HostName &amp;lt;PVE_TS_IP&amp;gt;
    User root
    IdentityFile ~&#x2F;.ssh&#x2F;proxmox
    IdentitiesOnly yes
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run &lt;code&gt;qm list&lt;&#x2F;code&gt;, &lt;code&gt;pct list&lt;&#x2F;code&gt;, and &lt;code&gt;pvesm status&lt;&#x2F;code&gt; first. The IDs below—100, 101, 103, and 200—are &lt;strong&gt;examples&lt;&#x2F;strong&gt;. Pick unused IDs and substitute your own bridge, storage, addresses, usernames, and public keys. Commands marked “host” run on Proxmox; commands marked “guest” run inside that guest. Reserve DHCP leases if you need stable LAN addresses.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;text&quot;&gt;workstation ── Tailscale&#x2F;SSH ── Proxmox (vmbr0)
                                  ├─ CT 200: Debian + &#x2F;dev&#x2F;kvm + Google Emulator
                                  ├─ VM 101: Windows 11 + RDP
                                  ├─ VM 100: Kali + SSH&#x2F;lab network
                                  └─ VM 103: Debian + Herdr server
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;1-official-android-emulator-in-an-unprivileged-container&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#1-official-android-emulator-in-an-unprivileged-container&quot; aria-label=&quot;Anchor link for: 1-official-android-emulator-in-an-unprivileged-container&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
1. Official Android Emulator in an unprivileged container&lt;&#x2F;h2&gt;
&lt;p&gt;The Android Emulator is itself a QEMU virtual machine. A Proxmox VM running it would normally require nested virtualization. Instead, place the emulator process in an &lt;strong&gt;unprivileged LXC&lt;&#x2F;strong&gt; and pass through only the host’s &lt;code&gt;&#x2F;dev&#x2F;kvm&lt;&#x2F;code&gt;. The container shares the host kernel; the Android guest uses KVM directly. This is not VM-grade isolation for hostile APKs.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;proxmox&#x2F;direct-kvm.gif&quot; alt=&quot;Animated diagram: the unprivileged emulator container receives the host KVM device and runs an Android AVD without a nested Proxmox VM.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;create-the-container&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#create-the-container&quot; aria-label=&quot;Anchor link for: create-the-container&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Create the container&lt;&#x2F;h3&gt;
&lt;p&gt;On the host, find the host KVM group ID and a Debian 13 template name:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;stat -c &amp;#39;%G %g %a&amp;#39; &#x2F;dev&#x2F;kvm
pveam available --section system | grep debian-13
pveam download local debian-13-standard_13.6-1_amd64.tar.zst
KVM_GID=$(stat -c %g &#x2F;dev&#x2F;kvm)
pct create 200 local:vztmpl&#x2F;debian-13-standard_13.6-1_amd64.tar.zst \
  --hostname android-emulator --unprivileged 1 \
  --cores 4 --memory 8192 --swap 1024 --rootfs local-lvm:64 \
  --net0 name=eth0,bridge=vmbr0,ip=dhcp,firewall=1 \
  --dev0 path=&#x2F;dev&#x2F;kvm,gid=&amp;quot;$KVM_GID&amp;quot;,mode=0660 \
  --onboot 1 --ostype debian
pct start 200
pct exec 200 -- ls -l &#x2F;dev&#x2F;kvm
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Use the actual template version shown by &lt;code&gt;pveam&lt;&#x2F;code&gt; if it changed. Do not enable privileged mode, &lt;code&gt;nesting=1&lt;&#x2F;code&gt;, broad device cgroups, or an unconfined AppArmor profile merely to get KVM access. The container’s &lt;code&gt;kvm&lt;&#x2F;code&gt; GID can differ from the host GID because of user-namespace mapping.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;install-google-s-sdk-and-create-an-avd&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#install-google-s-sdk-and-create-an-avd&quot; aria-label=&quot;Anchor link for: install-google-s-sdk-and-create-an-avd&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Install Google’s SDK and create an AVD&lt;&#x2F;h3&gt;
&lt;p&gt;Enter the container with &lt;code&gt;pct enter 200&lt;&#x2F;code&gt;. The following commands run &lt;strong&gt;inside CT 200 as root&lt;&#x2F;strong&gt;, except where &lt;code&gt;runuser&lt;&#x2F;code&gt; selects the dedicated &lt;code&gt;emulator&lt;&#x2F;code&gt; account:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;apt-get update
apt-get install -y --no-install-recommends ca-certificates curl unzip \
  openjdk-21-jre-headless libglib2.0-0 libnss3 libdbus-1-3 \
  libfontconfig1 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \
  libxcursor1 libxi6 libxtst6 libxrandr2 libxrender1 \
  libxkbcommon0 libxkbcommon-x11-0 libpulse0 libasound2t64 \
  libopengl0 libgl1 libegl1 libgbm1 libvulkan1 openssh-server nftables
useradd -m -s &#x2F;bin&#x2F;bash -G kvm emulator
install -d -o emulator -g emulator &#x2F;opt&#x2F;android-sdk &#x2F;opt&#x2F;android-sdk&#x2F;cmdline-tools
curl -fL https:&#x2F;&#x2F;dl.google.com&#x2F;android&#x2F;repository&#x2F;commandlinetools-linux-15859902_latest.zip \
  -o &#x2F;tmp&#x2F;android-commandline-tools.zip
printf &amp;#39;%s  %s\n&amp;#39; \
  &amp;#39;4e4c464f145a7512b57d088ac6c278c03c9eea610886b35a5e0804e74eedf583&amp;#39; \
  &#x2F;tmp&#x2F;android-commandline-tools.zip | sha256sum -c -
unzip -q &#x2F;tmp&#x2F;android-commandline-tools.zip -d &#x2F;opt&#x2F;android-sdk&#x2F;cmdline-tools
mv &#x2F;opt&#x2F;android-sdk&#x2F;cmdline-tools&#x2F;cmdline-tools &#x2F;opt&#x2F;android-sdk&#x2F;cmdline-tools&#x2F;latest
chown -R emulator:emulator &#x2F;opt&#x2F;android-sdk
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That ZIP and hash are a pinned, tested example; use a newer version and its verified checksum if Google changes the download. Run the SDK commands with an explicit home and SDK path:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;export HOME=&#x2F;home&#x2F;emulator ANDROID_HOME=&#x2F;opt&#x2F;android-sdk ANDROID_SDK_ROOT=&#x2F;opt&#x2F;android-sdk
cd &#x2F;home&#x2F;emulator
yes | runuser -u emulator -- env HOME=&amp;quot;$HOME&amp;quot; ANDROID_HOME=&amp;quot;$ANDROID_HOME&amp;quot; \
  ANDROID_SDK_ROOT=&amp;quot;$ANDROID_SDK_ROOT&amp;quot; \
  &#x2F;opt&#x2F;android-sdk&#x2F;cmdline-tools&#x2F;latest&#x2F;bin&#x2F;sdkmanager --licenses
runuser -u emulator -- env HOME=&amp;quot;$HOME&amp;quot; ANDROID_HOME=&amp;quot;$ANDROID_HOME&amp;quot; \
  ANDROID_SDK_ROOT=&amp;quot;$ANDROID_SDK_ROOT&amp;quot; \
  &#x2F;opt&#x2F;android-sdk&#x2F;cmdline-tools&#x2F;latest&#x2F;bin&#x2F;sdkmanager --install \
  &amp;#39;platform-tools&amp;#39; &amp;#39;emulator&amp;#39; &amp;#39;platforms;android-36&amp;#39; \
  &amp;#39;system-images;android-36;google_apis;x86_64&amp;#39;
printf &amp;#39;no\n&amp;#39; | runuser -u emulator -- env HOME=&amp;quot;$HOME&amp;quot; \
  ANDROID_HOME=&amp;quot;$ANDROID_HOME&amp;quot; ANDROID_SDK_ROOT=&amp;quot;$ANDROID_SDK_ROOT&amp;quot; \
  &#x2F;opt&#x2F;android-sdk&#x2F;cmdline-tools&#x2F;latest&#x2F;bin&#x2F;avdmanager create avd \
  -n Pixel_9_API_36 -k &amp;#39;system-images;android-36;google_apis;x86_64&amp;#39; \
  -d pixel_9 -c 4096M
runuser -u emulator -- &#x2F;opt&#x2F;android-sdk&#x2F;emulator&#x2F;emulator -accel-check
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This creates a Google APIs Android 16&#x2F;API 36 x86-64 Pixel profile. Google APIs and Google Play images are different packages; select the image your app requires. &lt;code&gt;-accel-check&lt;&#x2F;code&gt; must report usable KVM before continuing.&lt;&#x2F;p&gt;
&lt;p&gt;Create &lt;code&gt;&#x2F;etc&#x2F;systemd&#x2F;system&#x2F;android-emulator.service&lt;&#x2F;code&gt; in the container:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;ini&quot;&gt;[Unit]
Description=Google Android Emulator (Pixel 9 &#x2F; API 36)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=emulator
Group=emulator
SupplementaryGroups=kvm
WorkingDirectory=&#x2F;home&#x2F;emulator
Environment=HOME=&#x2F;home&#x2F;emulator
Environment=ANDROID_HOME=&#x2F;opt&#x2F;android-sdk
Environment=ANDROID_AVD_HOME=&#x2F;home&#x2F;emulator&#x2F;.android&#x2F;avd
Environment=QT_QPA_PLATFORM=offscreen
ExecStart=&#x2F;opt&#x2F;android-sdk&#x2F;emulator&#x2F;emulator -avd Pixel_9_API_36 -port 5554 -no-window -no-audio -gpu software -no-snapshot -no-boot-anim -accel on -no-metrics
Restart=on-failure
RestartSec=15
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;systemctl daemon-reload
systemctl enable --now android-emulator.service
journalctl -u android-emulator -b --no-pager -n 50
ss -lnt | grep -E &amp;#39;:555[45] &amp;#39;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The verified setup listened on &lt;strong&gt;container loopback&lt;&#x2F;strong&gt; ports 5554&#x2F;5555. Check your own listener; do not assume a headless emulator is firewalled by default.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;connect-android-studio-without-exposing-adb&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#connect-android-studio-without-exposing-adb&quot; aria-label=&quot;Anchor link for: connect-android-studio-without-exposing-adb&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Connect Android Studio without exposing ADB&lt;&#x2F;h3&gt;
&lt;p&gt;In the dedicated container, create a key-only SSH forwarding account. Replace the sample public key below with the workstation’s &lt;strong&gt;public&lt;&#x2F;strong&gt; key, never its private key:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;useradd -m -s &#x2F;usr&#x2F;sbin&#x2F;nologin adb-tunnel
install -d -m 0700 -o adb-tunnel -g adb-tunnel &#x2F;home&#x2F;adb-tunnel&#x2F;.ssh
printf &amp;#39;%s\n&amp;#39; &amp;#39;restrict,port-forwarding,permitopen=&amp;quot;127.0.0.1:5555&amp;quot; ssh-ed25519 REPLACE_WITH_PUBLIC_KEY workstation&amp;#39; \
  &amp;gt;&amp;gt; &#x2F;home&#x2F;adb-tunnel&#x2F;.ssh&#x2F;authorized_keys
chown adb-tunnel:adb-tunnel &#x2F;home&#x2F;adb-tunnel&#x2F;.ssh&#x2F;authorized_keys
chmod 0600 &#x2F;home&#x2F;adb-tunnel&#x2F;.ssh&#x2F;authorized_keys
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In &lt;code&gt;&#x2F;etc&#x2F;ssh&#x2F;sshd_config.d&#x2F;90-adb-tunnel.conf&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;text&quot;&gt;PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
AllowUsers adb-tunnel
Match User adb-tunnel
    AllowTcpForwarding local
    PermitOpen 127.0.0.1:5555
    AllowAgentForwarding no
    X11Forwarding no
    PermitTTY no
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;ssh -N&lt;&#x2F;code&gt; forwarding does not require a login shell. Validate with &lt;code&gt;sshd -t&lt;&#x2F;code&gt; and restart &lt;code&gt;ssh&lt;&#x2F;code&gt;. Allow container SSH &lt;strong&gt;only from the Proxmox bridge IP&lt;&#x2F;strong&gt; in the container firewall; leave ADB and emulator-console ports closed. For this dedicated CT, use an nftables input policy that drops unsolicited inbound traffic. Replace &lt;code&gt;&amp;lt;PVE_BRIDGE_IP&amp;gt;&lt;&#x2F;code&gt; before loading; merge with any existing rules rather than overwriting a shared policy:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;nft&quot;&gt;# &#x2F;etc&#x2F;nftables.conf inside the dedicated container
table inet android_ct {
    chain input {
        type filter hook input priority 10; policy drop;
        iifname &amp;quot;lo&amp;quot; accept
        ct state established,related accept
        ip saddr &amp;lt;PVE_BRIDGE_IP&amp;gt; tcp dport 22 accept
        udp sport 67 udp dport 68 accept
        ip protocol icmp accept
        ip6 nexthdr ipv6-icmp accept
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Validate with &lt;code&gt;nft -c -f &#x2F;etc&#x2F;nftables.conf&lt;&#x2F;code&gt;, then &lt;code&gt;systemctl enable --now nftables&lt;&#x2F;code&gt;. Do this from the Proxmox console so a mistaken rule cannot strand SSH. &lt;code&gt;firewall=1&lt;&#x2F;code&gt; on a Proxmox NIC is not a substitute for checking whether the host firewall is actually enabled.&lt;&#x2F;p&gt;
&lt;p&gt;On the workstation, with &lt;code&gt;pve&lt;&#x2F;code&gt; defined as a key-authenticated SSH alias for Proxmox:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;ssh -J pve -N -o ExitOnForwardFailure=yes \
  -L 127.0.0.1:15555:127.0.0.1:5555 adb-tunnel@&amp;lt;CT_LAN_IP&amp;gt;
# In another terminal, using the same adb installation Android Studio uses:
adb connect 127.0.0.1:15555
adb devices -l
adb -s 127.0.0.1:15555 shell getprop ro.build.version.release
scrcpy --serial 127.0.0.1:15555 --no-audio
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Android Studio’s device selector recognized the TCP-connected AVD, but IDE Run&#x2F;Debug with an app was not tested. A headless remote AVD need not appear in Studio’s embedded Running Devices panel; &lt;code&gt;scrcpy&lt;&#x2F;code&gt; supplies interactive video and input. If the tunnel drops, reopen it and repeat &lt;code&gt;adb connect&lt;&#x2F;code&gt;. On one Nix workstation, a blank &lt;code&gt;scrcpy&lt;&#x2F;code&gt; window required a native SDL2 rather than &lt;code&gt;sdl2-compat&lt;&#x2F;code&gt;; this was a &lt;strong&gt;client rendering&lt;&#x2F;strong&gt; problem, not an absent Android display.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-windows-vm-with-private-rdp&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#2-windows-vm-with-private-rdp&quot; aria-label=&quot;Anchor link for: 2-windows-vm-with-private-rdp&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
2. Windows VM with private RDP&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;proxmox&#x2F;windows-rdp.gif&quot; alt=&quot;Animated diagram: RDP reaches a Windows VM through a Tailscale-only Proxmox relay while the VM keeps running after the workstation disconnects.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Create a Windows 11 &lt;strong&gt;Pro&lt;&#x2F;strong&gt; VM in the Proxmox UI: OVMF&#x2F;UEFI, a TPM 2.0 state disk, 4 cores, 4 GiB RAM or more, and a 64 GiB disk. Attach an official Windows ISO and the VirtIO driver ISO. &lt;code&gt;e1000&lt;&#x2F;code&gt; networking and SATA storage work without loading VirtIO drivers during setup; VirtIO network&#x2F;storage are preferable once their drivers are installed. Put the NIC on &lt;code&gt;vmbr0&lt;&#x2F;code&gt;, finish Windows setup, install the QEMU guest agent, and enable it in the VM Options. Provide a valid Windows license.&lt;&#x2F;p&gt;
&lt;p&gt;In Windows, open &lt;strong&gt;Settings → System → Remote Desktop&lt;&#x2F;strong&gt;, enable it, leave Network Level Authentication on, and grant access only to intended accounts with strong passwords. Confirm from Proxmox that the guest answers on TCP 3389 before adding a relay. A working bridge and DHCP lease are separate requirements; if the VM has only a &lt;code&gt;169.254.x.x&lt;&#x2F;code&gt; address, fix DHCP&#x2F;router connectivity before assuming a static address will provide Internet.&lt;&#x2F;p&gt;
&lt;p&gt;To avoid Tailscale inside Windows and avoid publishing RDP, use Proxmox as a narrow TCP relay. Install &lt;code&gt;socat&lt;&#x2F;code&gt; on the &lt;strong&gt;host&lt;&#x2F;strong&gt;, find the VM’s IPv6 link-local address from &lt;code&gt;ipconfig &#x2F;all&lt;&#x2F;code&gt; in Windows, and confirm Proxmox can reach &lt;code&gt;[fe80::…%vmbr0]:3389&lt;&#x2F;code&gt;. Replace all placeholders in this systemd unit, including the Tailscale source IP. In systemd files, write a literal &lt;code&gt;%&lt;&#x2F;code&gt; as &lt;code&gt;%%&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;ini&quot;&gt;# &#x2F;etc&#x2F;systemd&#x2F;system&#x2F;windows-rdp-relay.service on Proxmox
[Unit]
Description=RDP from one Tailscale workstation to Windows
After=network-online.target tailscaled.service
Wants=network-online.target

[Service]
Type=simple
DynamicUser=yes
ExecStart=&#x2F;usr&#x2F;bin&#x2F;socat TCP4-LISTEN:3389,bind=&amp;lt;PVE_TS_IP&amp;gt;,reuseaddr,fork,range=&amp;lt;LAPTOP_TS_IP&amp;gt;&#x2F;32 TCP6:[fe80::GUEST_LINK_LOCAL%%vmbr0]:3389
Restart=always
RestartSec=10
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
CapabilityBoundingSet=
RestrictAddressFamilies=AF_INET AF_INET6

[Install]
WantedBy=multi-user.target
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;# Proxmox host, after replacing placeholders:
apt-get install socat
systemctl daemon-reload
systemctl enable --now windows-rdp-relay.service
ss -lnt | grep &amp;#39;:3389 &amp;#39;
# Workstation; enter the password at the RDP prompt:
xfreerdp3 &#x2F;v:&amp;lt;PVE_TS_IP&amp;gt;:3389 &#x2F;u:&amp;lt;WINDOWS_USER&amp;gt; &#x2F;cert:tofu +dynamic-resolution
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The relay binds only to Proxmox’s Tailscale address and accepts only the specified workstation. Windows still authenticates the RDP session. If a guest’s link-local IPv6 or the workstation’s Tailscale address changes, update the unit. Do not use &lt;code&gt;&#x2F;p:&amp;lt;password&amp;gt;&lt;&#x2F;code&gt; on a shared command line. A localhost-only SSH &lt;code&gt;-L&lt;&#x2F;code&gt; tunnel is an alternative if you do not want a persistent listener.&lt;&#x2F;p&gt;
&lt;p&gt;For a remote PowerShell shell, Windows WinRM can be reached through a &lt;strong&gt;separate localhost-only SSH tunnel&lt;&#x2F;strong&gt; to the guest’s port 5985. Restrict the Windows firewall rule to Proxmox’s bridge addresses and use authenticated, message-encrypted WinRM; do not bind a WinRM relay to the whole tailnet. &lt;code&gt;Enable-PSRemoting&lt;&#x2F;code&gt; configures a listener, service, and firewall rules. Local administrator authentication may separately require a &lt;code&gt;LocalAccountTokenFilterPolicy&lt;&#x2F;code&gt; change, which affects other network logons; assess that security trade-off before changing it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;3-kali-vm-and-an-optional-routed-lab&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#3-kali-vm-and-an-optional-routed-lab&quot; aria-label=&quot;Anchor link for: 3-kali-vm-and-an-optional-routed-lab&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
3. Kali VM and an optional routed lab&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;proxmox&#x2F;kali-lab.gif&quot; alt=&quot;Animated diagram: a workstation reaches Kali through Proxmox, with an optional routed lab subnet behind Kali.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Download the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.kali.org&#x2F;get-kali&#x2F;&quot;&gt;official Kali installer ISO&lt;&#x2F;a&gt; and verify its published checksum&#x2F;signature. Create a QEMU VM with a disk of at least 20 GiB (40 GiB leaves room for tools), 2 or more cores, at least 2 GiB RAM (4 GiB for a desktop), and a &lt;code&gt;vmbr0&lt;&#x2F;code&gt; NIC. Install through the Proxmox console, update packages, set a &lt;strong&gt;unique&lt;&#x2F;strong&gt; password, and install&#x2F;enable SSH if remote terminal access is wanted:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;# Kali guest
sudo apt update
sudo apt install openssh-server qemu-guest-agent
sudo systemctl enable --now ssh qemu-guest-agent
ip -4 addr show
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The Kali VM is a full VM, not a privileged container. Keep pentest traffic confined to networks you own or have permission to assess. The simplest workstation path needs &lt;strong&gt;no&lt;&#x2F;strong&gt; advertised subnet and no inbound LAN port on Proxmox:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;ssh -J pve &amp;lt;KALI_USER&amp;gt;@&amp;lt;KALI_LAN_IP&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If Kali hosts a separate lab subnet, for example &lt;code&gt;10.6.6.0&#x2F;24&lt;&#x2F;code&gt; with Kali at &lt;code&gt;10.6.6.1&lt;&#x2F;code&gt;, configure that subnet &lt;strong&gt;inside Kali first&lt;&#x2F;strong&gt; and confirm Proxmox can reach &lt;code&gt;10.6.6.1&lt;&#x2F;code&gt;. The tested host routes it through Kali’s IPv6 link-local address on &lt;code&gt;vmbr0&lt;&#x2F;code&gt;; a different lab topology needs a different next hop. Before enabling forwarding, restrict it with a host firewall policy for the intended source and destination. On Proxmox, enable IPv4 forwarding (and persist &lt;code&gt;net.ipv4.ip_forward=1&lt;&#x2F;code&gt; in your &lt;code&gt;&#x2F;etc&#x2F;sysctl.d&#x2F;&lt;&#x2F;code&gt; configuration):&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;sysctl -w net.ipv4.ip_forward=1
ip route replace 10.6.6.0&#x2F;24 via inet6 fe80::KALI_LINK_LOCAL dev vmbr0
ping -c 2 10.6.6.1
# Include any existing advertised routes in the comma-separated list:
tailscale set --advertise-routes=10.6.6.0&#x2F;24
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Approve &lt;code&gt;10.6.6.0&#x2F;24&lt;&#x2F;code&gt; under &lt;strong&gt;Machines → Proxmox → Subnets → Edit&lt;&#x2F;strong&gt; in the Tailscale admin console. Grant the intended user&#x2F;device access to the CIDR in tailnet policy. On a Linux workstation:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;sudo tailscale set --accept-routes=true
ip route show table 52 | grep 10.6.6
ping 10.6.6.1
ssh &amp;lt;KALI_USER&amp;gt;@10.6.6.1
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Route approval, tailnet ACLs, Proxmox’s forwarding firewall, and Kali’s own firewall are &lt;strong&gt;four different controls&lt;&#x2F;strong&gt;. A narrow host rule for a single workstation can be placed before Tailscale’s general forwarding chain, then persisted in a firewall configuration or a systemd unit:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;# Proxmox host; replace the address and persist the rule across reboots.
iptables -I FORWARD 1 -i tailscale0 -d 10.6.6.0&#x2F;24 \
  ! -s &amp;lt;LAPTOP_TS_IP&amp;gt;&#x2F;32 -j DROP
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This rule only drops non-matching sources arriving on &lt;code&gt;tailscale0&lt;&#x2F;code&gt;; it is not a complete forwarding policy and does not itself allow the permitted source. Check the remaining &lt;code&gt;FORWARD&lt;&#x2F;code&gt; rules and other ingress paths.&lt;&#x2F;p&gt;
&lt;p&gt;In one deployment, the route was approved and installed, but an old Proxmox &lt;code&gt;FORWARD&lt;&#x2F;code&gt; rule still dropped packets from the workstation’s &lt;em&gt;new&lt;&#x2F;em&gt; Tailscale IP. Diagnose in order: &lt;code&gt;ip route get 10.6.6.1&lt;&#x2F;code&gt; on the workstation, &lt;code&gt;tcpdump -ni tailscale0 host 10.6.6.1&lt;&#x2F;code&gt; on Proxmox, then &lt;code&gt;tcpdump -ni vmbr0 host 10.6.6.1&lt;&#x2F;code&gt; and &lt;code&gt;iptables -L FORWARD -v -n --line-numbers&lt;&#x2F;code&gt;. If packets enter &lt;code&gt;tailscale0&lt;&#x2F;code&gt; but not &lt;code&gt;vmbr0&lt;&#x2F;code&gt;, inspect the host forwarding rules; &lt;strong&gt;do not restart Tailscale just to refresh a route&lt;&#x2F;strong&gt;. Restrict forwarding to the intended source, and persist the forwarding setting, host route, and firewall rule across reboot. Tailscale’s default subnet-route SNAT usually avoids a separate return route; if SNAT is disabled, add one explicitly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;4-debian-vm-for-a-persistent-herdr-server&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#4-debian-vm-for-a-persistent-herdr-server&quot; aria-label=&quot;Anchor link for: 4-debian-vm-for-a-persistent-herdr-server&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
4. Debian VM for a persistent Herdr server&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;herdr.dev&#x2F;&quot;&gt;Herdr&lt;&#x2F;a&gt; owns terminal sessions on the machine doing the work. The client can detach over SSH while the server and its panes remain in the VM. A VM provides a separate kernel, disk, credentials, and resource limit for coding agents. One working allocation is 2 vCPUs, 2 GiB RAM, and a 32 GiB disk; increase it for large builds.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;proxmox&#x2F;herdr-detach.gif&quot; alt=&quot;Animated diagram: a Herdr client detaches through an SSH jump while its remote server and panes remain in the Debian VM.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Use an official &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloud.debian.org&#x2F;images&#x2F;cloud&#x2F;trixie&#x2F;latest&#x2F;&quot;&gt;Debian 13 cloud image&lt;&#x2F;a&gt; and Proxmox Cloud-Init. On the host, after selecting an &lt;strong&gt;unused&lt;&#x2F;strong&gt; VM ID and copying in your workstation’s &lt;strong&gt;public&lt;&#x2F;strong&gt; SSH key:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;curl -fL https:&#x2F;&#x2F;cloud.debian.org&#x2F;images&#x2F;cloud&#x2F;trixie&#x2F;latest&#x2F;debian-13-genericcloud-amd64.qcow2 \
  -o &#x2F;root&#x2F;debian-13-genericcloud-amd64.qcow2
qm create 103 --name herdr-agent --cores 2 --memory 2048 \
  --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-pci \
  --ostype l26 --agent enabled=1 --onboot 1
qm set 103 --scsi0 local-lvm:0,import-from=&#x2F;root&#x2F;debian-13-genericcloud-amd64.qcow2
qm resize 103 scsi0 32G
qm set 103 --ide2 local-lvm:cloudinit --boot order=scsi0 \
  --serial0 socket --vga serial0 --ciuser agent \
  --sshkey &#x2F;root&#x2F;herdr-vm.pub --ipconfig0 ip=dhcp
qm start 103
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Check the downloaded image’s published checksum before using it. Cloud-Init’s &lt;code&gt;--sshkey&lt;&#x2F;code&gt; reads a file &lt;strong&gt;on Proxmox&lt;&#x2F;strong&gt;, not on the workstation. Find the DHCP lease from the router or VM console, then connect with &lt;code&gt;ssh -J pve -i ~&#x2F;.ssh&#x2F;herdr-vm agent@&amp;lt;HERDR_LAN_IP&amp;gt;&lt;&#x2F;code&gt;. Verify the SSH host key from the console before trusting a reused DHCP address. Install &lt;code&gt;qemu-guest-agent&lt;&#x2F;code&gt; inside the guest and enable it; then &lt;code&gt;qm agent 103 network-get-interfaces&lt;&#x2F;code&gt; can report its current address.&lt;&#x2F;p&gt;
&lt;p&gt;Install Herdr as the unprivileged &lt;code&gt;agent&lt;&#x2F;code&gt; user from an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;herdrdev&#x2F;herdr&#x2F;releases&quot;&gt;official release&lt;&#x2F;a&gt; or the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;herdr.dev&#x2F;docs&#x2F;install&#x2F;&quot;&gt;official installer&lt;&#x2F;a&gt;. If using the installer, download and inspect it before running it:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;# Herdr VM, logged in as agent
curl -fsSLo &#x2F;tmp&#x2F;herdr-install.sh https:&#x2F;&#x2F;herdr.dev&#x2F;install.sh
less &#x2F;tmp&#x2F;herdr-install.sh
sh &#x2F;tmp&#x2F;herdr-install.sh
~&#x2F;.local&#x2F;bin&#x2F;herdr --version
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Run the server as a user service rather than root. As &lt;code&gt;agent&lt;&#x2F;code&gt;, create &lt;code&gt;~&#x2F;.config&#x2F;systemd&#x2F;user&#x2F;herdr.service&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;ini&quot;&gt;[Unit]
Description=Herdr server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
Environment=PATH=&#x2F;home&#x2F;agent&#x2F;.local&#x2F;bin:&#x2F;usr&#x2F;local&#x2F;bin:&#x2F;usr&#x2F;bin:&#x2F;bin
ExecStart=&#x2F;home&#x2F;agent&#x2F;.local&#x2F;bin&#x2F;herdr server
Restart=on-failure
RestartSec=5

[Install]
WantedBy=default.target
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;# Guest: enable lingering with sudo, then as agent:
sudo loginctl enable-linger agent
systemctl --user daemon-reload
systemctl --user enable --now herdr.service
systemctl --user status herdr.service
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;On the workstation, add another entry to &lt;code&gt;~&#x2F;.ssh&#x2F;config&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;sshconfig&quot;&gt;Host herdr-vm
    HostName &amp;lt;HERDR_LAN_IP&amp;gt;
    User agent
    IdentityFile ~&#x2F;.ssh&#x2F;herdr-vm
    IdentitiesOnly yes
    ProxyJump pve
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Verify &lt;code&gt;ssh herdr-vm&lt;&#x2F;code&gt; first. Install the Herdr client locally and run either:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;herdr --remote herdr-vm        # attach one remote session
herdr machine add herdr-vm     # save it alongside local&#x2F;other machines
herdr                       # open the multi-machine UI
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Herdr may ask before installing or replacing an incompatible remote binary. Do not approve replacement of a running server until you understand whether its pane processes will stop. &lt;code&gt;Ctrl+B&lt;&#x2F;code&gt;, then &lt;code&gt;Q&lt;&#x2F;code&gt;, detaches the client; the remote server keeps panes alive. After a &lt;strong&gt;VM reboot&lt;&#x2F;strong&gt;, the original processes do not survive: Herdr restores saved layout and can resume supported agent sessions, not arbitrary running programs. Back up the guest home directory and disk separately.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;operations-that-apply-to-all-four&quot;&gt;&lt;a class=&quot;header-anchor no-hover-padding&quot; href=&quot;#operations-that-apply-to-all-four&quot; aria-label=&quot;Anchor link for: operations-that-apply-to-all-four&quot;&gt;&lt;span class=&quot;link-icon&quot; aria-hidden=&quot;true&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;a&gt;
Operations that apply to all four&lt;&#x2F;h2&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;# Proxmox host
pct status 200 &amp;amp;&amp;amp; pct exec 200 -- systemctl is-active android-emulator
qm status 100 &amp;amp;&amp;amp; qm status 101 &amp;amp;&amp;amp; qm status 103
pvesm status
# Point this at a backup-capable storage target with sufficient space:
vzdump 200 100 101 103 --storage &amp;lt;BACKUP_STORAGE&amp;gt; --mode snapshot --compress zstd
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A Proxmox &lt;strong&gt;snapshot is not a backup&lt;&#x2F;strong&gt;: it depends on the same storage pool. Install and run the guest agent where supported, monitor thin-pool free space, schedule &lt;code&gt;vzdump&lt;&#x2F;code&gt; to separate storage, and test a restore. Keep service listeners bound to Tailscale or loopback and update address-based firewall rules when DHCP or Tailscale IPs change.&lt;&#x2F;p&gt;
&lt;p&gt;References: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pve.proxmox.com&#x2F;wiki&#x2F;Cloud-Init_Support&quot;&gt;Proxmox Cloud-Init&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.kali.org&#x2F;docs&#x2F;virtualization&#x2F;install-proxmox-guest-vm&#x2F;&quot;&gt;Kali on Proxmox&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows-server&#x2F;remote&#x2F;remote-desktop-services&#x2F;remotepc&#x2F;remote-desktop-allow-access&quot;&gt;Windows RDP requirements&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tailscale.com&#x2F;docs&#x2F;reference&#x2F;route-injection&quot;&gt;Tailscale route injection&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;herdr.dev&#x2F;docs&#x2F;persistence-remote&#x2F;&quot;&gt;Herdr remote attach&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
        <summary type="html">Run an Android Emulator, Windows desktop, Kali lab, and Herdr agent on Proxmox instead of squeezing them onto a laptop. The host supplies the CPU, RAM, and storage; the guests keep running when the laptop disconnects or shuts down…</summary>
        </entry>
</feed>
