<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: adrien</title>
    <description>The latest articles on Forem by adrien (@adrien77570).</description>
    <link>https://forem.com/adrien77570</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2638199%2F02fe2bb6-de20-432b-8197-0da146453d04.png</url>
      <title>Forem: adrien</title>
      <link>https://forem.com/adrien77570</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/adrien77570"/>
    <language>en</language>
    <item>
      <title>Running Docker in LXC Containers: Why It Makes Sense for Homelabs</title>
      <dc:creator>adrien</dc:creator>
      <pubDate>Mon, 09 Mar 2026 12:43:34 +0000</pubDate>
      <link>https://forem.com/adrien77570/running-docker-in-lxc-containers-why-it-makes-sense-for-homelabs-31fa</link>
      <guid>https://forem.com/adrien77570/running-docker-in-lxc-containers-why-it-makes-sense-for-homelabs-31fa</guid>
      <description>&lt;p&gt;If you spend any time in Proxmox communities, you'll see this debate come up weekly: "Should I run Docker in an LXC container or in a VM?"&lt;/p&gt;

&lt;p&gt;The VM purists will tell you LXC + Docker is cursed. The pragmatists will tell you it works fine. After running this setup for over a year on my homelab, here's my take.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Short Answer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For homelabs and small deployments: LXC + Docker works great.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For production with untrusted workloads: use VMs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Run Docker in LXC?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Resource Efficiency
&lt;/h3&gt;

&lt;p&gt;An LXC container has virtually no overhead. It's just process isolation using Linux namespaces and cgroups — the same technology Docker uses internally.&lt;/p&gt;

&lt;p&gt;A typical VM setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VM overhead: 512MB-1GB RAM just for the guest OS&lt;/li&gt;
&lt;li&gt;Disk overhead: 2-5GB for the base system&lt;/li&gt;
&lt;li&gt;CPU overhead: hypervisor context switching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An LXC container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RAM overhead: ~50MB&lt;/li&gt;
&lt;li&gt;Disk overhead: ~200MB&lt;/li&gt;
&lt;li&gt;CPU overhead: negligible (native execution)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you're running 10+ services on a homelab server with 32GB RAM, that overhead adds up.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Simpler Backup and Migration
&lt;/h3&gt;

&lt;p&gt;Proxmox can snapshot LXC containers instantly. No guest agent needed, no quiescing issues. The backup is just a tarball of the filesystem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Backup an LXC container&lt;/span&gt;
vzdump 100 &lt;span class="nt"&gt;--compress&lt;/span&gt; zstd &lt;span class="nt"&gt;--mode&lt;/span&gt; snapshot

&lt;span class="c"&gt;# Restore it&lt;/span&gt;
pct restore 101 /var/lib/vz/dump/vzdump-lxc-100-&lt;span class="k"&gt;*&lt;/span&gt;.tar.zst
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare that to VM backups where you need guest agents, proper shutdown sequences, and larger backup files.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Direct Hardware Access
&lt;/h3&gt;

&lt;p&gt;Need to pass through a USB device or access host storage? LXC makes it trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Mount a host directory&lt;/span&gt;
pct &lt;span class="nb"&gt;set &lt;/span&gt;100 &lt;span class="nt"&gt;-mp0&lt;/span&gt; /mnt/data,mp&lt;span class="o"&gt;=&lt;/span&gt;/data

&lt;span class="c"&gt;# Pass through a device&lt;/span&gt;
lxc.cgroup2.devices.allow: c 189:&lt;span class="k"&gt;*&lt;/span&gt; rwm
lxc.mount.entry: /dev/bus/usb dev/bus/usb none &lt;span class="nb"&gt;bind&lt;/span&gt;,optional,create&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No IOMMU groups, no PCI passthrough complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating a Docker-Ready LXC Container
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the container (Debian 12)&lt;/span&gt;
pct create 100 &lt;span class="nb"&gt;local&lt;/span&gt;:vztmpl/debian-12-standard_12.2-1_amd64.tar.zst &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--hostname&lt;/span&gt; docker-host &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--memory&lt;/span&gt; 4096 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--cores&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--rootfs&lt;/span&gt; local-lvm:20 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--net0&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eth0,bridge&lt;span class="o"&gt;=&lt;/span&gt;vmbr0,ip&lt;span class="o"&gt;=&lt;/span&gt;dhcp &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--features&lt;/span&gt; &lt;span class="nv"&gt;nesting&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1,keyctl&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--unprivileged&lt;/span&gt; 1

&lt;span class="c"&gt;# Start it&lt;/span&gt;
pct start 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;nesting=1&lt;/code&gt;: Required for Docker to create its own namespaces&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;keyctl=1&lt;/code&gt;: Needed for some container operations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unprivileged 1&lt;/code&gt;: Security best practice&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installing Docker
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enter the container&lt;/span&gt;
pct enter 100

&lt;span class="c"&gt;# Install Docker (official method)&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://get.docker.com | sh

&lt;span class="c"&gt;# Verify it works&lt;/span&gt;
docker run hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No special configuration needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling AppArmor Issues
&lt;/h3&gt;

&lt;p&gt;Some Docker images fail with AppArmor errors in LXC. The fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On the Proxmox host, edit the container config&lt;/span&gt;
nano /etc/pve/lxc/100.conf

&lt;span class="c"&gt;# Add this line&lt;/span&gt;
lxc.apparmor.profile: unconfined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restart the container. This is safe for homelabs; in production you'd write proper AppArmor profiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Security?
&lt;/h2&gt;

&lt;p&gt;The main argument against Docker-in-LXC is security. Let's break it down:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The risk&lt;/strong&gt;: If someone escapes the Docker container, they're in an LXC container, not a full VM. LXC provides less isolation than a hypervisor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The reality for homelabs&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You control what containers you run&lt;/li&gt;
&lt;li&gt;You're not running untrusted code&lt;/li&gt;
&lt;li&gt;The attack surface is your own services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use VMs instead&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running untrusted workloads&lt;/li&gt;
&lt;li&gt;Multi-tenant environments&lt;/li&gt;
&lt;li&gt;Compliance requirements&lt;/li&gt;
&lt;li&gt;When you need different kernels&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a homelab running Jellyfin, Home Assistant, and some self-hosted apps? LXC + Docker is perfectly fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Comparison
&lt;/h2&gt;

&lt;p&gt;I ran some basic benchmarks comparing the same Docker Compose stack (PostgreSQL + Redis + a Node.js app) in three configurations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Memory Used&lt;/th&gt;
&lt;th&gt;Startup Time&lt;/th&gt;
&lt;th&gt;Disk I/O&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bare metal Docker&lt;/td&gt;
&lt;td&gt;1.2 GB&lt;/td&gt;
&lt;td&gt;8s&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LXC + Docker&lt;/td&gt;
&lt;td&gt;1.3 GB&lt;/td&gt;
&lt;td&gt;9s&lt;/td&gt;
&lt;td&gt;~Same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VM + Docker&lt;/td&gt;
&lt;td&gt;2.1 GB&lt;/td&gt;
&lt;td&gt;45s&lt;/td&gt;
&lt;td&gt;-15%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The LXC overhead is negligible. The VM overhead is noticeable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tips From Production Use
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Use Docker Compose, not Portainer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Portainer adds complexity. A &lt;code&gt;docker-compose.yml&lt;/code&gt; file is version-controlled, reproducible, and doesn't need a web UI running 24/7.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. One service per LXC container&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't cram everything into one container. Use separate LXC containers for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Media stack (Jellyfin, *arr apps)&lt;/li&gt;
&lt;li&gt;Home automation (Home Assistant)&lt;/li&gt;
&lt;li&gt;Development (databases, test apps)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way you can snapshot, migrate, and resource-limit each independently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Set resource limits&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Limit memory and CPU&lt;/span&gt;
pct &lt;span class="nb"&gt;set &lt;/span&gt;100 &lt;span class="nt"&gt;--memory&lt;/span&gt; 4096 &lt;span class="nt"&gt;--cores&lt;/span&gt; 2

&lt;span class="c"&gt;# Set swap limit&lt;/span&gt;
pct &lt;span class="nb"&gt;set &lt;/span&gt;100 &lt;span class="nt"&gt;--swap&lt;/span&gt; 512
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without limits, a runaway container can affect your entire host.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Use ZFS or LVM thin provisioning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Snapshots before updates are lifesavers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Before updating containers&lt;/span&gt;
pct snapshot 100 before-update

&lt;span class="c"&gt;# If something breaks&lt;/span&gt;
pct rollback 100 before-update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Docker in LXC isn't a hack or an anti-pattern. It's a pragmatic choice that trades some isolation for significant resource savings.&lt;/p&gt;

&lt;p&gt;For homelabs: &lt;strong&gt;go for it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For production with compliance requirements or untrusted workloads: &lt;strong&gt;use VMs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Proxmox forums will keep debating this forever, but thousands of homelabs run this setup without issues. Try it yourself and see.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your Proxmox setup? Running Docker in LXC, VMs, or bare metal? Drop a comment below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>proxmox</category>
      <category>docker</category>
      <category>selfhosted</category>
      <category>homelab</category>
    </item>
  </channel>
</rss>
