<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Docker on blog.rymcg.tech</title>
    <link>https://blog.rymcg.tech/tags/docker/</link>
    <description>Recent content in Docker on blog.rymcg.tech</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <copyright>Copyright © 2020-2026, EnigmaCurry</copyright>
    <lastBuildDate>Fri, 23 Jan 2026 23:00:00 -0600</lastBuildDate><atom:link href="https://blog.rymcg.tech/tags/docker/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>NixOS VMs part 2: Bootstrapping a Docker server with immutable NixOS on Proxmox</title>
      <link>https://blog.rymcg.tech/blog/linux/nixos-proxmox-vm/</link>
      <pubDate>Fri, 23 Jan 2026 23:00:00 -0600</pubDate>
      
      <guid>https://blog.rymcg.tech/blog/linux/nixos-proxmox-vm/</guid>
      <description>&lt;p&gt;&lt;em&gt;This is part 2 of a series on &lt;a href=&#34;https://github.com/EnigmaCurry/nixos-vm-template&#34;&gt;nixos-vm-template&lt;/a&gt;:&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;&lt;a href=&#34;https://blog.rymcg.tech/blog/linux/code-agent-vm/&#34;&gt;Running code agents in an immutable NixOS VM&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Bootstrapping a Docker server with immutable NixOS on Proxmox (this post)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href=&#34;https://blog.rymcg.tech/blog/linux/mutable-vms/&#34;&gt;Mutable VMs are cool too&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href=&#34;https://blog.rymcg.tech/blog/linux/nixos-vm-home-manager/&#34;&gt;Managing VMs with home-manager and sway-home&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;In the &lt;a href=&#34;http://blog.rymcg.tech/blog/linux/code-agent-vm/&#34;&gt;last
post&lt;/a&gt; I
described running AI code agents inside immutable NixOS VMs using
libvirt on a laptop. That setup works well for local development, but
sometimes you want to deploy VMs on actual infrastructure - a Proxmox
server sitting in a closet, a rack, or someone else&amp;rsquo;s datacenter.&lt;/p&gt;
&lt;p&gt;The
&lt;a href=&#34;https://github.com/EnigmaCurry/nixos-vm-template&#34;&gt;nixos-vm-template&lt;/a&gt;
project now supports multiple backends. You build the same NixOS
images locally on your workstation, you run the same commands as you
would with libvirt, and you get the same immutable VMs - the only
difference is where they land. This post walks through using the
Proxmox backend to bootstrap a Docker server VM.&lt;/p&gt;
&lt;h2 id=&#34;why-proxmox&#34;&gt;Why Proxmox?&lt;/h2&gt;
&lt;p&gt;Libvirt is fine for laptops and &amp;lsquo;sworkstations&amp;rsquo;. You run &lt;code&gt;just create&lt;/code&gt;, the VM shows up locally, and you SSH into it. But if you want
VMs running 24/7 on dedicated hardware, you probably have a hypervisor
already, and in the homelab world that hypervisor is usually Proxmox.&lt;/p&gt;
&lt;p&gt;The new Proxmox backend builds images locally with Nix, then ships
them to your PVE node over SSH. No Proxmox API tokens, no web UI
clicking, no cloud-init templates. Just NixOS config, SSH, and &lt;code&gt;qm&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-backend-abstraction&#34;&gt;The backend abstraction&lt;/h2&gt;
&lt;p&gt;The tool now has a &lt;code&gt;BACKEND&lt;/code&gt; variable. Set it to &lt;code&gt;libvirt&lt;/code&gt; or
&lt;code&gt;proxmox&lt;/code&gt; and the same Justfile recipes work against either target:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Local libvirt (the default)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just create
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Remote Proxmox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BACKEND&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;proxmox just create
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;just create&lt;/code&gt; command runs an interactive configuration wizard
that prompts for the VM name, profiles, memory, CPUs, disk size, and
network mode. After configuration, it builds the image, creates the
VM, and starts it automatically.&lt;/p&gt;
&lt;p&gt;You can also put &lt;code&gt;BACKEND=proxmox&lt;/code&gt; in a &lt;code&gt;.env&lt;/code&gt; file and forget about
it. The commands are identical - &lt;code&gt;just stop&lt;/code&gt;, &lt;code&gt;just upgrade&lt;/code&gt;, &lt;code&gt;just ssh&lt;/code&gt; - they just talk to different backends.&lt;/p&gt;
&lt;h2 id=&#34;set-up-the-proxmox-connection&#34;&gt;Set up the Proxmox connection&lt;/h2&gt;
&lt;p&gt;All you need is SSH access to your PVE node. Create a &lt;code&gt;.env&lt;/code&gt; file in
the project root:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BACKEND&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;proxmox
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PVE_HOST&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;pve
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PVE_NODE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;pve
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PVE_STORAGE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;local-zfs
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PVE_BRIDGE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;vmbr0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PVE_DISK_FORMAT&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;raw
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PVE_BACKUP_STORAGE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;pbs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A few notes on these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;BACKEND&lt;/code&gt; must be set to proxmox, otherwise libvirt is the default.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PVE_HOST&lt;/code&gt; must be the name of the SSH config entry in your &lt;code&gt;~/.ssh/config&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PVE_NODE&lt;/code&gt; must match your Proxmox node&amp;rsquo;s actual hostname. If your
node is called &lt;code&gt;pve&lt;/code&gt; in the web UI, put &lt;code&gt;pve&lt;/code&gt; here.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PVE_STORAGE&lt;/code&gt; is which storage system the VM disks get stored on
(i.e., &lt;code&gt;local&lt;/code&gt;, &lt;code&gt;local-zfs&lt;/code&gt;, &lt;code&gt;my-nfs&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PVE_BRIDGE&lt;/code&gt; is the default network bridge. You can override this
per-VM.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PVE_DISK_FORMAT&lt;/code&gt; use &lt;code&gt;raw&lt;/code&gt; format for ZFS or LVM-thin, &lt;code&gt;qcow2&lt;/code&gt; for
directory/NFS storage.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PVE_BACKUP_STORAGE&lt;/code&gt; is optional, for vzdump backups. Point it at a
PBS instance if you have one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make sure you can SSH to the PVE node. Use a key without a password,
or make sure that your SSH agent is loaded so you don&amp;rsquo;t need to type
the password:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ssh -i ~/.ssh/id_ed25519 root@192.168.1.100
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If that works, you&amp;rsquo;re good.&lt;/p&gt;
&lt;h2 id=&#34;build-and-create-the-docker-vm&#34;&gt;Build and create the Docker VM&lt;/h2&gt;
&lt;p&gt;The project ships with composable mixin profiles. The &lt;code&gt;docker&lt;/code&gt; profile
includes the Docker daemon and adds users to the &lt;code&gt;docker&lt;/code&gt; group. For a
dedicated Docker server, you don&amp;rsquo;t need the development tools or the
AI agents that we used in the last post. The &lt;code&gt;docker&lt;/code&gt; profile gives
you a minimal system with SSH, Docker, and not much else (the &lt;code&gt;core&lt;/code&gt;
profile is always implicitly included).&lt;/p&gt;
&lt;p&gt;Run the interactive create command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just create
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The wizard prompts you for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VM name&lt;/strong&gt;: e.g., &lt;code&gt;apps01&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Profiles&lt;/strong&gt;: select &lt;code&gt;docker&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory&lt;/strong&gt;: e.g., &lt;code&gt;4096&lt;/code&gt; (4GB)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPUs&lt;/strong&gt;: e.g., &lt;code&gt;2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Var disk size&lt;/strong&gt;: e.g., &lt;code&gt;50G&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network mode&lt;/strong&gt;: select &lt;code&gt;bridge&lt;/code&gt; and enter &lt;code&gt;vmbr0&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After configuration, &lt;code&gt;just create&lt;/code&gt; builds the NixOS image with a
read-only root filesystem and Docker enabled, transfers it to your PVE
node via rsync, imports it as a Proxmox VM, and starts it
automatically.&lt;/p&gt;
&lt;h2 id=&#34;first-boot&#34;&gt;First boot&lt;/h2&gt;
&lt;p&gt;The VM starts automatically after &lt;code&gt;just create&lt;/code&gt; completes. Check its
status:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just status apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The status command queries the QEMU guest agent for the VM&amp;rsquo;s IP
address. Once it&amp;rsquo;s up:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just ssh admin@apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;re now SSH&amp;rsquo;d into a minimal NixOS system with Docker running.
Verify it works:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run --rm hello-world
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;docker-data-persistence&#34;&gt;Docker data persistence&lt;/h2&gt;
&lt;p&gt;The immutable root design means Docker&amp;rsquo;s data directory
(&lt;code&gt;/var/lib/docker&lt;/code&gt;) lives on the read-write &lt;code&gt;/var&lt;/code&gt; disk. This is where
images, containers, volumes, and networks are stored. When you upgrade
the base image, all of this survives.&lt;/p&gt;
&lt;p&gt;If you want to run containers with persistent data, use standard
Docker volumes, or you may use bind mounts from another directory
under &lt;code&gt;/var&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;firewall-configuration&#34;&gt;Firewall configuration&lt;/h2&gt;
&lt;p&gt;By default, ports 22, 80, and 443 are open. The firewall rules are
stored per-VM in &lt;code&gt;machines/&amp;lt;name&amp;gt;/tcp_ports&lt;/code&gt; (one port per line). To
open additional ports:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;8080&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; machines/apps01/tcp_ports
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just upgrade apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The upgrade syncs the new port list to the VM&amp;rsquo;s identity files and
rebuilds the boot image, and automatically reboots.&lt;/p&gt;
&lt;p&gt;Important note: The firewall rules are applied inside the VM &lt;em&gt;and&lt;/em&gt; on
the Proxmox host. This is a defense-in-depth approach. However, this
also means that you should never manually touch the firewall config of
the VM on the Proxmox console. All firewall changes must happen in the
&lt;code&gt;machines/&amp;lt;name&amp;gt;/tcp_ports&lt;/code&gt; and &lt;code&gt;machines/&amp;lt;name&amp;gt;/udp_ports&lt;/code&gt; files, and
subsequently run &lt;code&gt;just upgrade&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;network-options&#34;&gt;Network options&lt;/h2&gt;
&lt;p&gt;Proxmox VMs can use any bridge configured on your PVE node. The
network mode is stored per-VM and can be changed after creation:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Move to a different bridge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just network-config apps01 bridge:vmbr1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just upgrade apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For NAT (if your PVE node has a NAT bridge configured):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just network-config apps01 nat
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just upgrade apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;snapshots-before-risky-changes&#34;&gt;Snapshots before risky changes&lt;/h2&gt;
&lt;p&gt;About to &lt;code&gt;docker system prune -a&lt;/code&gt; and hoping you don&amp;rsquo;t regret it?
Snapshot first:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just snapshot apps01 before-prune
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If things go wrong:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just stop apps01
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just restore-snapshot apps01 before-prune
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just start apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;backups&#34;&gt;Backups&lt;/h2&gt;
&lt;p&gt;For proper backups (not just snapshots), the tool wraps Proxmox&amp;rsquo;s
vzdump:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just backup apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates a compressed backup on your &lt;code&gt;PVE_BACKUP_STORAGE&lt;/code&gt;. To
restore:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just restore-backup apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you have a Proxmox Backup Server, point &lt;code&gt;PVE_BACKUP_STORAGE&lt;/code&gt; at it
and you get incremental, deduplicated backups for free.&lt;/p&gt;
&lt;h2 id=&#34;upgrades&#34;&gt;Upgrades&lt;/h2&gt;
&lt;p&gt;The immutable design makes upgrades straightforward. Say you want to
add
&lt;a href=&#34;https://github.com/jesseduffield/lazydocker&#34;&gt;lazydocker&lt;/a&gt;
(A TUI manager for Docker). Edit the profile:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-nix&#34; data-lang=&#34;nix&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# profiles/docker.nix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ config&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; pkgs&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt; }:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  config &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    virtualisation&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;docker&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;enable &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    users&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;users&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;core&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;adminUser&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;extraGroups &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;docker&amp;#34;&lt;/span&gt; ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    users&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;users&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;core&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;regularUser&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;extraGroups &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [ &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;docker&amp;#34;&lt;/span&gt; ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    environment&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;systemPackages &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;with&lt;/span&gt; pkgs; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      lazydocker
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then upgrade the VM:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just upgrade apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This rebuilds the image, syncs identity files, replaces the boot disk
on Proxmox, and restarts the VM. Your &lt;code&gt;/var&lt;/code&gt; disk - including all
Docker data, volumes, and home directories - is untouched. The VM
comes back up with the app &lt;code&gt;lazydocker&lt;/code&gt; available and all your
containers intact.&lt;/p&gt;
&lt;h2 id=&#34;cloning&#34;&gt;Cloning&lt;/h2&gt;
&lt;p&gt;Need another Docker server with the same setup? Clone it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just clone apps01 apps02
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The clone command prompts for the new VM name and optionally lets you
adjust hardware settings (memory, CPUs, network) while keeping the
same profile and disk contents.&lt;/p&gt;
&lt;p&gt;This makes a full clone of the VM on Proxmox, generates fresh identity
files (new hostname, machine-id, MAC address, SSH host keys), and
syncs them onto the cloned &lt;code&gt;/var&lt;/code&gt; disk. You get an independent VM
that&amp;rsquo;s otherwise identical to the original.&lt;/p&gt;
&lt;h2 id=&#34;serial-console&#34;&gt;Serial console&lt;/h2&gt;
&lt;p&gt;If networking is broken and SSH won&amp;rsquo;t connect, you can attach to the
VM&amp;rsquo;s serial console directly:&lt;/p&gt;
&lt;p&gt;First you&amp;rsquo;ll need to set the root password (root login is disabled
otherwise):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just passwd apps01
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just upgrade apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reboot, and you&amp;rsquo;ll be able to login via the serial console:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just console apps01
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This opens an SSH session to the Proxmox node and attaches to the VM&amp;rsquo;s
serial port. You may need to press &lt;code&gt;Enter&lt;/code&gt; to see the login prompt.
Exit with &lt;code&gt;Ctrl-O&lt;/code&gt; (that&amp;rsquo;s the letter O, not zero).&lt;/p&gt;
&lt;h2 id=&#34;proxmox-uses-full-clones-of-the-disk-image&#34;&gt;Proxmox uses full clones of the disk image&lt;/h2&gt;
&lt;p&gt;Unlike the libvirt backend, which has thin provisioning of the boot
device, the Proxmox backend sends a full clone of the boot device for
each VM you create on Proxmox. This increases the disk space required
per VM by about 3 to 4 GB. You can run as many Docker servers as you
want from a single &lt;code&gt;just build docker&lt;/code&gt;, but, on Proxmox, each &lt;code&gt;just create&lt;/code&gt; produces an independent VM with its own boot disk, its own
identity, and its own &lt;code&gt;/var&lt;/code&gt; disk.&lt;/p&gt;
&lt;p&gt;If you really do want thin provisioning of Proxmox VMs, you could use
nixos-vm-template to create the first VM, then turn that VM into a
Proxmox template, and then use the Proxmox clone feature in the
console, but then those clones would not be managed by
nixos-vm-template.&lt;/p&gt;
&lt;h2 id=&#34;syncing-identity-files-into-var&#34;&gt;Syncing identity files into /var&lt;/h2&gt;
&lt;p&gt;The Proxmox backend&amp;rsquo;s identity sync is worth noting: during upgrades,
it mounts the &lt;code&gt;/var&lt;/code&gt; disk on the PVE node using &lt;code&gt;qemu-nbd&lt;/code&gt; and writes
identity files directly. No need to download a multi-gigabyte disk
just to update a hostname.&lt;/p&gt;
&lt;h2 id=&#34;putting-it-together&#34;&gt;Putting it together&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the full workflow for going from zero to a running Docker
server on Proxmox:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# One-time setup on your workstation:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://github.com/EnigmaCurry/nixos-vm-template
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd nixos-vm-template
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cat &amp;gt; .env &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;lt;&amp;lt; &amp;#39;EOF&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;BACKEND=proxmox
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;PVE_HOST=pve
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;PVE_NODE=pve
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;PVE_STORAGE=local-zfs
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;PVE_DISK_FORMAT=raw
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;PVE_BRIDGE=vmbr0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Build and deploy (interactive wizard)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just create
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Enter: apps01, docker, 4096, 2, 50G, bridge, vmbr0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# The VM starts automatically, then SSH in:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;just ssh admin@apps01
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# On the VM&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -d --name nginx -p 80:80 nginx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you have an immutable NixOS machine for your Docker server,
running on your own Proxmox infrastructure, built from a declarative
Nix configuration, with snapshots and backups available, upgradeable
without losing data, plus even if you don&amp;rsquo;t make backups, the whole
machine is reproducible from the Nix config in your git repository
(minus your data).&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
