<?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>DEV Community: Norm Dong</title>
    <description>The latest articles on DEV Community by Norm Dong (@beedog).</description>
    <link>https://dev.to/beedog</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%2F865686%2Fd32d5b5b-d7fc-4c96-b3e6-bd598128347b.png</url>
      <title>DEV Community: Norm Dong</title>
      <link>https://dev.to/beedog</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/beedog"/>
    <language>en</language>
    <item>
      <title>Portainer with Traefik, automatic HTTPS and protecting your online services with CloudFlare Access in a home lab</title>
      <dc:creator>Norm Dong</dc:creator>
      <pubDate>Sun, 26 Jan 2025 06:10:51 +0000</pubDate>
      <link>https://dev.to/beedog/portainer-with-traefik-automatic-https-and-protecting-your-online-services-with-cloudflare-access-57f8</link>
      <guid>https://dev.to/beedog/portainer-with-traefik-automatic-https-and-protecting-your-online-services-with-cloudflare-access-57f8</guid>
      <description>&lt;p&gt;This post documents some parts of my home lab setup on which I run a bunch of VM and container workload. These may not be the best setup but they work for me, and hopefully it will help you too. Every piece of software and service mentioned is free, except for the domain name.&lt;/p&gt;

&lt;p&gt;I use the &lt;code&gt;docker-compose.yml&lt;/code&gt; file from &lt;a href="https://docs.portainer.io/advanced/reverse-proxy/traefik" rel="noopener noreferrer"&gt;Portainer's doc&lt;/a&gt; to installing Portainer with Traefik. On top of it, I have a few extra things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="c1"&gt;# For Traefik container&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--api=true&lt;/span&gt; &lt;span class="c1"&gt;# Enable the dashboard&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--api.dashboard=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--api.insecure=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--providers.file.filename=/etc/traefik/dynamic.yml&lt;/span&gt; &lt;span class="c1"&gt;# Add a dynamic config file&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--providers.file.watch=true&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# The dynamic config file sits in the same folder with the docker-compose.yaml file&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./dynamic.yml:/etc/traefik/dynamic.yml:ro"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For container workloads, I tend to run them as a docker-compose stack. I add the same labels as the Portainer's docker compose file. Specifically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;xxxx&lt;/span&gt; &lt;span class="c1"&gt;# Remember to put the container in the same network as Traefik&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.servicename.rule=Host(`sub.domain.notcom`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.servicename.entrypoints=websecure"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.servicename.service=servicename"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.servicename.tls.certresolver=leresolver"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.servicename.loadbalancer.server.port=xxxx"&lt;/span&gt; &lt;span class="c1"&gt;# The port on the container, NOT the mapped port on the host&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I run &lt;a href="https://kasmweb.com/" rel="noopener noreferrer"&gt;Kasm Workspaces&lt;/a&gt; on its own VM, its web UI uses a self-signed certificate, I need to tell Traefik to not verify that certificate in my dynamic config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;routers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kasm-route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Host(`sub.domain.notcom`)"&lt;/span&gt;
      &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kasm&lt;/span&gt;
      &lt;span class="na"&gt;entryPoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;websecure&lt;/span&gt;
      &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;certresolver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;leresolver"&lt;/span&gt;

  &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kasm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;loadBalancer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://10.10.10.10"&lt;/span&gt;
        &lt;span class="na"&gt;serversTransport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kasmTransport&lt;/span&gt;

  &lt;span class="na"&gt;serversTransports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kasmTransport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;insecureSkipVerify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# Internal Server Error if self-signed certificate is not skipped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use CloudFlare Access to protect some of the services. In this particular setup, I add GitHub to my login methods, I won't get into the details of the setup, as of now it's under "Zero Trust" -&amp;gt; "Settings" -&amp;gt; "Authentication" -&amp;gt; "Login methods" -&amp;gt; "Add new" -&amp;gt; select "GitHub". CloudFlare shows detailed steps of setting it up.&lt;/p&gt;

&lt;p&gt;Then, go back to "Zero Trust" -&amp;gt; "Access" -&amp;gt; "Applications", setup a self-hosted application on the domain. For the access policy, it can be as simple as a single Include rule:&lt;br&gt;
Selector: Emails.&lt;br&gt;
Value: The email address of the GitHub account you are going to login with. We wouldn't want to let anyone who has a GitHub account in, would we?&lt;/p&gt;

&lt;p&gt;Because Traefik automatically sets up HTTPS on the server with Let's Encrypt, you can set CloudFlare to full strict mode, rather than full - which does encrypt the traffic between CloudFlare and your server but does not verify the server's certificate.&lt;/p&gt;

&lt;p&gt;That's it!&lt;/p&gt;

</description>
      <category>traefik</category>
      <category>portainer</category>
      <category>docker</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>Managing Proxmox VMs and LXCs with Terraform</title>
      <dc:creator>Norm Dong</dc:creator>
      <pubDate>Fri, 17 Jan 2025 02:03:05 +0000</pubDate>
      <link>https://dev.to/beedog/managing-proxmox-vms-and-lxcs-with-terraform-l5i</link>
      <guid>https://dev.to/beedog/managing-proxmox-vms-and-lxcs-with-terraform-l5i</guid>
      <description>&lt;p&gt;There are a few Proxmox provider implementations available on the Terraform registry, this post specifically uses this one: &lt;a href="https://registry.terraform.io/providers/loeken/proxmox/latest/docs" rel="noopener noreferrer"&gt;https://registry.terraform.io/providers/loeken/proxmox/latest/docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other implementations might behave slightly differently but I haven't tried. This particular implementation has a weird behavior: it tries to recreate the disk for a virtual machine when running &lt;code&gt;terraform apply&lt;/code&gt; - which then fails on the server's side. Fortunately it doesn't stop me from creating more resources e.g. when I add a new LXC to the file, it will still create the LXC just fine, so it kinda "works". A new disk volume will be created and scheduled to replace a VM's disk if it's running (since it doesn't hot swap), which needs to be manually reverted and deleted.&lt;/p&gt;

&lt;p&gt;Compared to other services that support IaC (e.g. an AWS EC2 instance), I'm not sure if it's this particular implementation, or if it's the API on Proxmox's side, the whole experience does not feel smooth. It wants to do some "updates" when I haven't changed a single letter, which then doesn't actually seem to change anything, again it mostly "works", and it's better than manually clicking through a few steps each time I want to create a new VM/LXC through the web UI, so it's good enough to me.&lt;/p&gt;

&lt;p&gt;My Terraform file looks like this, hopefully it's helpful to you if you are trying to do something similar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;proxmox&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"loeken/proxmox"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;=2.9.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 0.14"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"proxmox"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# Default HTTPS port of Proxmox is 8006, yours might be different&lt;/span&gt;
  &lt;span class="nx"&gt;pm_api_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://xxxx:8006/api2/json"&lt;/span&gt;
  &lt;span class="c1"&gt;# I thought this was required as my Proxmox server uses a self-signed certificate. But apparently it works without this set to true anyway&lt;/span&gt;
  &lt;span class="c1"&gt;#pm_tls_insecure = true&lt;/span&gt;
  &lt;span class="c1"&gt;# Obviously not the best practice, you should use environment variables PM_USER and PM_PASS instead. I only do this because I am in a home lab environment&lt;/span&gt;
  &lt;span class="nx"&gt;pm_user&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"root@pam"&lt;/span&gt;
  &lt;span class="nx"&gt;pm_password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"secureultrapluspromax"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# https://registry.terraform.io/providers/loeken/proxmox/latest/docs/resources/vm_qemu&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"proxmox_vm_qemu"&lt;/span&gt; &lt;span class="s2"&gt;"resource_name_here"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VM name here"&lt;/span&gt;
  &lt;span class="nx"&gt;target_node&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Name of the node (under the Datacenter node)"&lt;/span&gt;
  &lt;span class="nx"&gt;vmid&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
  &lt;span class="nx"&gt;cores&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
  &lt;span class="nx"&gt;memory&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;65536&lt;/span&gt; &lt;span class="c1"&gt;# MiB&lt;/span&gt;
  &lt;span class="nx"&gt;os_type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&lt;/span&gt;
  &lt;span class="c1"&gt;# The volume name under the target node "local", then the storage type "ISO Images", then the image's name&lt;/span&gt;
  &lt;span class="nx"&gt;iso&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local:iso/ubuntu-24.10-live-server-amd64.iso"&lt;/span&gt;

  &lt;span class="nx"&gt;disk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scsi"&lt;/span&gt;
    &lt;span class="nx"&gt;size&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"50G"&lt;/span&gt;
    &lt;span class="nx"&gt;storage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local-lvm"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bridge&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vmbr0"&lt;/span&gt;
    &lt;span class="nx"&gt;firewall&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="nx"&gt;link_down&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="nx"&gt;model&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"e1000"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# https://registry.terraform.io/providers/loeken/proxmox/latest/docs/resources/lxc&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"proxmox_lxc"&lt;/span&gt; &lt;span class="s2"&gt;"resource_name_here"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;target_node&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Name of the node (under the Datacenter node)"&lt;/span&gt;
  &lt;span class="nx"&gt;hostname&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hostname"&lt;/span&gt;
  &lt;span class="c1"&gt;# The volume name under the target node "local", then the storage type "CT Templates", then the template's name&lt;/span&gt;
  &lt;span class="nx"&gt;ostemplate&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst"&lt;/span&gt;
  &lt;span class="c1"&gt;# Root password&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"secureultrapluspromax"&lt;/span&gt;
  &lt;span class="nx"&gt;unprivileged&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;vmid&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;
  &lt;span class="nx"&gt;cores&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
  &lt;span class="nx"&gt;memory&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt; &lt;span class="c1"&gt;# MiB&lt;/span&gt;

  &lt;span class="c1"&gt;// Terraform will crash without rootfs defined&lt;/span&gt;
  &lt;span class="nx"&gt;rootfs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;storage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"local-lvm"&lt;/span&gt;
    &lt;span class="nx"&gt;size&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2G"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;features&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nfs"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Comments from the provider's doc, I keep it here to remind myself of this weird bug&lt;/span&gt;
  &lt;span class="c1"&gt;# // NFS share mounted on host&lt;/span&gt;
  &lt;span class="c1"&gt;# // Without 'volume' defined, Proxmox will try to create a volume with&lt;/span&gt;
  &lt;span class="c1"&gt;# // the value of 'storage' + : + 'size' (without the trailing G) - e.g.&lt;/span&gt;
  &lt;span class="c1"&gt;# // "/srv/host/bind-mount-point:256".&lt;/span&gt;
  &lt;span class="c1"&gt;# // This behaviour looks to be caused by a bug in the provider.&lt;/span&gt;
  &lt;span class="nx"&gt;mountpoint&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt;
    &lt;span class="nx"&gt;slot&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;storage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/mnt/mountpoint"&lt;/span&gt;
    &lt;span class="nx"&gt;volume&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/mnt/mountpoint"&lt;/span&gt;
    &lt;span class="nx"&gt;mp&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/mnt/data"&lt;/span&gt;
    &lt;span class="c1"&gt;# Unintuitively (and if I remember correctly), this does not work without specifying a size&lt;/span&gt;
    &lt;span class="nx"&gt;size&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1T"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;network&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eth0"&lt;/span&gt;
    &lt;span class="nx"&gt;bridge&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vmbr0"&lt;/span&gt;
    &lt;span class="nx"&gt;ip&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dhcp"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>proxmox</category>
      <category>terraform</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Learning Ansible, Proxmox and LXC, Part 1</title>
      <dc:creator>Norm Dong</dc:creator>
      <pubDate>Sun, 27 Oct 2024 05:00:38 +0000</pubDate>
      <link>https://dev.to/beedog/learning-ansible-proxmox-and-lxc-part-1-2hbp</link>
      <guid>https://dev.to/beedog/learning-ansible-proxmox-and-lxc-part-1-2hbp</guid>
      <description>&lt;p&gt;I've been running a server at home for a few years and gone through many iterations of hardware and different software/virtualization stacks. On this iteration I landed on Proxmox which supports LXC, an interesting virtualization technology.&lt;/p&gt;

&lt;p&gt;Now, I have been a fan of IaC and I wanted to learn Ansible for a while. I'd prefer Terraform but the Terraform module for Proxmox did not work very well when I tried it a while ago, so it's a good opportunity for me to try Ansible!&lt;/p&gt;

&lt;p&gt;This blog series is my learning notes for using Ansible to provision LXCs (and a few VMs) and configure the software/service running in said LXCs.&lt;/p&gt;

&lt;p&gt;Or so I thought - the series is no more, but I'm keeping the "Part 1" in the title just for the lols, here is what happened:&lt;/p&gt;

&lt;p&gt;Step 1, I create a snippet to quickly get an LXC up, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test playbook&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;

  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create new container with minimal options&lt;/span&gt;
    &lt;span class="na"&gt;community.general.proxmox&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;vmid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;
      &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-proxmox-node&lt;/span&gt;
      &lt;span class="na"&gt;api_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ansible-usr@pve&lt;/span&gt;
      &lt;span class="na"&gt;api_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;123&lt;/span&gt;
      &lt;span class="na"&gt;api_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.x.x&lt;/span&gt;
      &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-lxc&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;456&lt;/span&gt;
      &lt;span class="na"&gt;ostemplate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst'&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-lvm&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I run &lt;code&gt;ansible-playbook playbook.yml&lt;/code&gt; to create the LXC. So far, so good.&lt;/p&gt;

&lt;p&gt;Step 2, the LXC is up and running but the config is quite loose. I'd like to tighten it up a bit. I add &lt;code&gt;disk_volume&lt;/code&gt; block to the template, replacing &lt;code&gt;storage&lt;/code&gt;. As per &lt;a href="https://docs.ansible.com/ansible/latest/collections/community/general/proxmox_module.html#examples" rel="noopener noreferrer"&gt;example in the documentation&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test playbook&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;

  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create new container with minimal options&lt;/span&gt;
    &lt;span class="na"&gt;community.general.proxmox&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;vmid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;
      &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-proxmox-node&lt;/span&gt;
      &lt;span class="na"&gt;api_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ansible-usr@pve&lt;/span&gt;
      &lt;span class="na"&gt;api_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;123&lt;/span&gt;
      &lt;span class="na"&gt;api_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.x.x&lt;/span&gt;
      &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-lxc&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;456&lt;/span&gt;
      &lt;span class="na"&gt;ostemplate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst'&lt;/span&gt;
      &lt;span class="na"&gt;disk_volume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-lvm&lt;/span&gt;
        &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What could possibly go wrong?&lt;/p&gt;

&lt;p&gt;Step 3, Ansible complains: "disk_volume is not a valid parameter". Huh? But the &lt;a href="https://docs.ansible.com/ansible/latest/collections/community/general/proxmox_module.html#parameter-disk_volume" rel="noopener noreferrer"&gt;doc&lt;/a&gt; contains the definition and examples, how could...oh, "added in community.general 9.2.0", I see. Let me upgrade my local Ansible version.&lt;/p&gt;

&lt;p&gt;Step 3.5 I'll spare you (and myself!) the details of me trying to figure out my local module's version, things are already bad enough.&lt;/p&gt;

&lt;p&gt;Step 4, now the error message is something like "400 Bad Request, disk_volume is not defined in schema". OK, maybe I need to upgrade and reboot my Proxmox installation as well. Too bad though, next step please!&lt;/p&gt;

&lt;p&gt;Step 5, OK I've given up on trying to make &lt;code&gt;disk_volume&lt;/code&gt; work. Let me revert that property and move on to the next thing, adding &lt;code&gt;cores&lt;/code&gt; property to limit the CPU resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test playbook&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;

  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create new container with minimal options&lt;/span&gt;
    &lt;span class="na"&gt;community.general.proxmox&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;vmid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;
      &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-proxmox-node&lt;/span&gt;
      &lt;span class="na"&gt;api_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ansible-usr@pve&lt;/span&gt;
      &lt;span class="na"&gt;api_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;123&lt;/span&gt;
      &lt;span class="na"&gt;api_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;192.168.x.x&lt;/span&gt;
      &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-lxc&lt;/span&gt;
      &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;456&lt;/span&gt;
      &lt;span class="na"&gt;ostemplate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst'&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-lvm&lt;/span&gt;
      &lt;span class="na"&gt;cores&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works like a charm. If we just ignore the whole &lt;code&gt;disk_volume&lt;/code&gt; thing for a second...&lt;/p&gt;

&lt;p&gt;Step 6, I want to try updating the LXC through Ansible. I change &lt;code&gt;cores&lt;/code&gt; from 4 to 6, save, rerun, surely this should update my LXC's CPU to 6, right? Right?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PLAY RECAP **********************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 7, I think it's time to revisit Terraform. Wish me luck!&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>iac</category>
      <category>proxmox</category>
      <category>lxc</category>
    </item>
  </channel>
</rss>
