JustToThePoint English Website Version
JustToThePoint en español

Using Terraform with Docker to Manage Proxmox VMs

I’d far rather be happy than right any day, Douglas Adams, The Hitchhiker’s Guide to the Galaxy

image info

Using Terraform to Manage Proxmox VMs

Terraform is an infrastructure as code tool that lets you build, change, and version your entirely infrastructure safely and efficiently. Users define and describe their desired state in declarative files (HCL or JSON), and Terraform figures out how to create or update resources. This includes compute instances, storage, networking, DNS records, and more.

This guide shows how to use Terraform to clone a Proxmox VM template into running VMs, fully automated and reproducible. This is the second article in our two-part series about Using Terraform to Manage Proxmox VMs and is a continuation of the first one, so if you haven’t taken a look at it yet, I recommend you read it first and come back.

Using Docker

Create Docker Compose configuration

We’ll install Docker CE from the official Docker repo (rather than the distro package, which may be out-of-date). Navigate to dockerdocks, Install, Debian, Install Docker Engine on Debian.

# Remove old Docker variants
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done

# 1. Update and install dependencies
sudo apt-get update
sudo apt-get install -y \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

# 2. Add Docker’s official GPG key and set up the repo
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 3. Install Docker Engine
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 4. Verify your install
docker run --rm hello-world

# 5. (Optional) If you want to manage Docker as a non-root user, add yourself to the docker group:
sudo usermod -aG docker $USER
newgrp docker

Community

docker-compose.yml for Terraform

Inside the project, add a new file docker-compose.yml with the following content (each line is explained below):

services:
  terraform: # The name of the service that will run Terraform.
    image: hashicorp/terraform:1.12.2
    # It sets the Docker image we want to use for our service, pin your exact version terraform --version
    volumes:
        - .:/terraform
    # Maps the current project directory to /terraform in the container.
    # Docker containers use virtualization to run in an environment that’s isolated from the host machine. As a result, containers don’t have access to the file system by default.
    # To get around this problem, we define - .:/terraform which creates volume mapping . (this is shorthand for the current project directory where the docker-compose.yml is located)
    # to /terraform inside the docker container.
    working_dir: /terraform
    # Sets the working directory inside the container.
    # It tells the Docker container to work from the /terraform directory (working directory) which we are mapping our code project to.
    # If they don't match, the container will not know where to look for the configuration files.
    network: host
    # So the container can communicate with the ProxMox server without any additional network setup.
    entrypoint: ["terraform"] # Specifies the command to run when the container starts.
    command: ["plan", "-var-file=credentials.auto.tfvars"]
    # Passes Terraform command and arguments to the container.
    # The docker compose command does not directly support Terraform's -var or -var-file flags.
    # Instead, we need to pass these arguments to the terraform command inside the container.

Therefore, anything inside my project directory (terraform) on the host is available under /terraform in the container. If id_rsa is in ./.ssh/id_rsa on my host (/root/terraform/.ssh/id_rsa), then in the container it’ll be at /terraform/.ssh/id_rsa.

private_key = file(var.private_key_path). Terraform will then look for /terraform/.ssh/id_rsa inside the container, which actually maps to ./.ssh/id_rsa (/root/terraform/.ssh/id_rsa) on my host.

Running Terraform via Docker

Initialize Providers and Backend. From your project root, run the following command to initialize Terraform:

# Initialize providers & backend
root@myserver:~/terraform# docker compose -f docker-compose.yml run --rm terraform init --upgrade

# Expected Output:
Initializing the backend...

Initializing provider plugins...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work.
[...]

# Preview changes
# Next, preview the changes that Terraform will make to your infrastructure:
root@myserver:~/terraform# docker compose -f docker-compose.yml run --rm terraform plan -out k8s-master.tfplan

# Expected Output:
No changes. Infrastructure is up-to-date.
# This message indicates that Terraform did not detect any differences between your configuration and the existing infrastructure, meaning no actions are needed at this time.

# If you are ready to apply the planned changes, execute the following command:
docker compose -f docker-compose.yml run --rm terraform apply "k8s-master.tfplan"

Additional notes:

  1. The ‐‐rm flag ensures that the Docker container is removed after the command finishes executing, keeping your environment clean.
  2. The -out k8s-master.tfplan argument specifies the filename for the plan, allowing you to apply it later.
  3. You should always carefully review the output of the plan command before applying changes to ensure that the expected modifications align with your intentions.
  4. When you run docker compose -f docker-compose.yml run ‐‐rm terraform ..., Docker creates a temporary container from the hashicorp/terraform image. Unless you explicitly mount or copy files into the container, it won’t see them.

In my docker-compose.yml, I mounted the current directory (., the project folder) to /terraform inside the container:

volumes:
  - .:/terraform

Automated Proxmox VM Management Script

This script automates the process of:

  1. Removing a previously created VM (if it exists).
  2. Running terraform commands (init, plan, and apply) using Docker Compose.
#!/bin/bash
# Make the script executable: chmod +x proxmox-terraform.sh
# Run the script: ./proxmox-terraform.sh

# Variables
VM_ID=207
DOCKER_COMPOSE_FILE="docker-compose.yml"
TERRAFORM_DIR="/root/terraform" # Update this path as needed

# Function to check if a VM exists
vm_exists() {
    qm status $VM_ID > /dev/null 2>&1
    return $?
}

# Remove the previously created VM if it exists
# Normally, you need to stop using Proxmox's GUI, VM, Shutdown (gracefully) or Stop.
# Sometimes, you need to stop a broken VM using: qm stop 207
if vm_exists; then
    echo "VM $VM_ID exists. Removing it..."
    qm stop $VM_ID --force > /dev/null 2>&1
    qm destroy $VM_ID --purge > /dev/null 2>&1
    echo "VM $VM_ID removed."
else
    echo "VM $VM_ID does not exist. Skipping removal."
fi

# Change to the Terraform directory
cd $TERRAFORM_DIR || { echo "Failed to change to directory $TERRAFORM_DIR"; exit 1; }

# Run Docker Compose commands
# When you are done editing the configuration files.
# Use Docker Compose to run Terraform commands in a containerized environment.
# The -f flag specifies which Docker Compose file to use, in this case, docker-compose.yml.
# The --rm flag ensures that the container is removed after the command completes.
# Initializes the Terraform environment.
echo "Running 'docker compose -f $DOCKER_COMPOSE_FILE run --rm terraform init'..."
docker compose -f $DOCKER_COMPOSE_FILE run --rm terraform init || { echo "Terraform init failed"; exit 1; }

# Generates and shows the execution plan, allowing you to see what actions Terraform will take.
echo "Running 'docker compose -f $DOCKER_COMPOSE_FILE run --rm terraform plan'..."
docker compose -f $DOCKER_COMPOSE_FILE run --rm terraform plan -out k8s-master.tfplan || { echo "Terraform plan failed"; exit 1; }

# Applies the changes to create the VM
echo "Running 'docker compose -f $DOCKER_COMPOSE_FILE run --rm terraform apply'..."
docker compose -f $DOCKER_COMPOSE_FILE run --rm terraform apply -auto-approve k8s-master.tfplan || { echo "Terraform apply failed"; exit 1; }

echo "Script completed successfully."

Some tips

  1. You can always install the full version of GNOME using the sudo apt install ubuntu-gnome-desktop command.
  2. Once the installation and configuration is done, you may want to take a screenshot of the VM: Select the main node (e.g., myserver), the recently created VM (e.g., ubuntu-oracular-desktop), select the Snapshots, and click on Take Snapshot

Biography

Bitcoin donation

JustToThePoint Copyright © 2011 - 2025 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun. Social Issues, Join us.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.