 Autor: [Adam Nadolny](/autorzy/adam-nadolny) Ekspert DevOps i infrastruktury · Zweryfikowano Kwiecień 2026

1.  [Strona główna](/) ›
2.  [Baza wiedzy](/baza-wiedzy/) ›
3.  Terraform — zarządzanie VPS jako kod

# Terraform — zarządzanie infrastrukturą VPS jako kod

Opublikowano: 9 kwietnia 2026 · Kategoria: VPS / DevOps

Klikanie w panelu dostawcy działa dla jednego serwera. Przy dwóch serwerach, firewallach, rekordach DNS i backupach zaczyna się chaos. Terraform rozwiązuje ten problem: opisujesz całą infrastrukturę w plikach tekstowych, wersjonujesz w git i odtwarzasz jedną komendą. Infrastructure as Code (IaC) to standard w każdym profesjonalnym projekcie VPS.

## Instalacja Terraform

Terraform to pojedynczy binarny plik. Instalacja na Ubuntu/Debian:

wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb \[signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg\] https://apt.releases.hashicorp.com $(lsb\_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

terraform version
# Terraform v1.7.x

## Struktura projektu Terraform

Typowy projekt Terraform dla VPS wygląda tak:

infra/
├── main.tf          # Główne zasoby (serwery, sieci)
├── variables.tf     # Deklaracje zmiennych
├── outputs.tf       # Wartości wyjściowe (IP, ID)
├── versions.tf      # Wymagane wersje providerów
├── terraform.tfvars # Wartości zmiennych (NIE commituj jeśli zawiera tokeny!)
└── .terraform.lock.hcl  # Lock file providerów (commituj)

## Provider Hetzner Cloud

Hetzner to popularny wybór dla polskich projektów — serwery w Niemczech (Frankfurt, Nuremberg), niskie ceny i oficjalny provider Terraform. Konfiguracja:

\# versions.tf
terraform {
  required\_version = ">= 1.5"
  required\_providers {
    hcloud = {
      source  = "hetznercloud/hcloud"
      version = "~> 1.47"
    }
  }
}

provider "hcloud" {
  token = var.hcloud\_token
}

\# variables.tf
variable "hcloud\_token" {
  description = "Hetzner Cloud API Token"
  type        = string
  sensitive   = true  # Nie pojawi się w logach
}

variable "server\_type" {
  description = "Typ serwera Hetzner (cx11, cx21, cx31...)"
  type        = string
  default     = "cx22"
}

variable "location" {
  description = "Lokalizacja serwera"
  type        = string
  default     = "nbg1"  # Nuremberg
}

\# main.tf — VPS na Hetzner
resource "hcloud\_ssh\_key" "default" {
  name       = "my-ssh-key"
  public\_key = file("~/.ssh/id\_rsa.pub")
}

resource "hcloud\_server" "web" {
  name        = "web-01"
  image       = "ubuntu-22.04"
  server\_type = var.server\_type
  location    = var.location
  ssh\_keys    = \[hcloud\_ssh\_key.default.id\]

  labels = {
    environment = "production"
    role        = "web"
  }

  # Cloud-init: podstawowa konfiguracja przy starcie
  user\_data = <<-EOT
    #cloud-config
    package\_update: true
    packages:
      - nginx
      - ufw
    runcmd:
      - ufw allow 22
      - ufw allow 80
      - ufw allow 443
      - ufw --force enable
  EOT
}

# Firewall
resource "hcloud\_firewall" "web" {
  name = "web-firewall"

  rule {
    direction = "in"
    protocol  = "tcp"
    port      = "22"
    source\_ips = \["0.0.0.0/0", "::/0"\]
  }

  rule {
    direction = "in"
    protocol  = "tcp"
    port      = "80"
    source\_ips = \["0.0.0.0/0", "::/0"\]
  }

  rule {
    direction = "in"
    protocol  = "tcp"
    port      = "443"
    source\_ips = \["0.0.0.0/0", "::/0"\]
  }
}

resource "hcloud\_firewall\_attachment" "web" {
  firewall\_id = hcloud\_firewall.web.id
  server\_ids  = \[hcloud\_server.web.id\]
}

\# outputs.tf
output "server\_ip" {
  description = "Publiczne IP serwera"
  value       = hcloud\_server.web.ipv4\_address
}

output "server\_id" {
  description = "ID serwera Hetzner"
  value       = hcloud\_server.web.id
}

## Provider DigitalOcean

DigitalOcean (Droplets) ma dojrzały provider z szerokim wsparciem dla Load Balancerów, Spaces (S3-compatible), Managed Databases i Kubernetes. Konfiguracja analogiczna do Hetzner:

\# versions.tf — DigitalOcean
terraform {
  required\_providers {
    digitalocean = {
      source  = "digitalocean/digitalocean"
      version = "~> 2.36"
    }
  }
}

provider "digitalocean" {
  token = var.do\_token
}

resource "digitalocean\_droplet" "web" {
  image    = "ubuntu-22-04-x64"
  name     = "web-01"
  region   = "fra1"  # Frankfurt
  size     = "s-1vcpu-1gb"
  ssh\_keys = \[digitalocean\_ssh\_key.default.fingerprint\]
}

resource "digitalocean\_ssh\_key" "default" {
  name       = "my-key"
  public\_key = file("~/.ssh/id\_rsa.pub")
}

## Cykl życia — plan, apply, destroy

\# 1. Inicjalizacja (jednorazowo lub po zmianie providera)
terraform init

# 2. Walidacja składni
terraform validate

# 3. Plan — co zostanie zrobione (NIE wykonuje zmian)
terraform plan

# 4. Apply — wykonaj zmiany (pyta o potwierdzenie)
terraform apply

# Lub bez pytania (CI/CD):
terraform apply -auto-approve

# 5. Sprawdź outputy
terraform output server\_ip

# 6. Zniszcz wszystko (OSTROŻNIE!)
terraform destroy

## Zarządzanie state — remote backend

Domyślnie state zapisywany jest lokalnie w `terraform.tfstate`. W teamie lub CI/CD użyj remote backend — np. S3 (AWS) lub Terraform Cloud:

\# backend.tf — S3 remote state
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/vps/terraform.tfstate"
    region         = "eu-central-1"
    encrypt        = true
    dynamodb\_table = "terraform-locks"  # State locking
  }
}

Backend

Zalety

Kiedy używać

local

Zero konfiguracji

Projekty osobiste, nauka

S3 + DynamoDB

Locking, szyfrowanie, tanie

Projekty AWS, CI/CD

GCS

Wbudowany locking

Projekty GCP

Terraform Cloud

UI, runs, sentinel policies

Teamy, enterprise

## Zmienne i .tfvars

\# terraform.tfvars (NIE commituj do gita jeśli zawiera tokeny!)
hcloud\_token = "your-hetzner-api-token"
server\_type  = "cx22"
location     = "nbg1"

# Lub przez zmienne środowiskowe (bezpieczniejsze dla tokenów):
export TF\_VAR\_hcloud\_token="your-hetzner-api-token"
terraform apply

## Best practices

-   **Zawsze sprawdzaj plan przed apply** — szczególnie gdy widzisz `-/+` (replace): Terraform zniszczy i odtworzy zasób, co może oznaczać utratę danych.
-   **Trzymaj state w remote backend** — lokalny state + kilka osób w teamie = konflikty i korupcja state.
-   **Używaj `sensitive = true`** dla tokenów i haseł — nie pojawią się w logach plan/apply.
-   **Moduły Terraform** — dla powtarzalnych zasobów (np. "serwer z firewallem i DNS") twórz moduły w katalogu `modules/`. Reużywalność i DRY.
-   **Lifecycle — prevent\_destroy** — chroni krytyczne zasoby (bazy danych) przed przypadkowym `terraform destroy`: dodaj blok `lifecycle { prevent_destroy = true }`.

## Najczęstsze pytania

Czym jest Terraform i do czego służy w kontekście VPS? +

Terraform to narzędzie Infrastructure as Code (IaC) od HashiCorp. Zamiast klikać w panelu dostawcy, opisujesz infrastrukturę w plikach .tf (język HCL), a Terraform tworzy, modyfikuje i usuwa zasoby. Dla VPS oznacza to: tworzenie serwerów, przypisywanie IP, konfiguracja firewalli, DNS — wszystko w wersjonowanym kodzie. Przy awarii serwera odtworzysz całą infrastrukturę jedną komendą.

Jakie są dostępne providery Terraform dla hostingu VPS? +

Najpopularniejsze providery: Hetzner Cloud (hetznercloud/hetzner), DigitalOcean (digitalocean/digitalocean), Vultr (vultr/vultr), Linode/Akamai (linode/linode), OVHcloud (ovh/ovh). W Polsce Hetzner jest najchętniej wybierany ze względu na serwery w Niemczech, niskie ceny i dobry provider Terraform. Każdy provider wymaga tokenu API, który podajesz przez zmienną środowiskową.

Co to jest Terraform state i dlaczego jest ważny? +

State (terraform.tfstate) to plik JSON przechowujący aktualny stan infrastruktury — mapowanie między zasobami Terraform a rzeczywistymi obiektami u dostawcy (np. ID serwera). Bez state Terraform nie wiedziałby co już istnieje. Nigdy nie commituj state do gita — może zawierać sekrety. W produkcji trzymaj state w S3, GCS lub Terraform Cloud (remote backend). Utrata state nie niszczy infrastruktury, ale Terraform traci kontrolę nad zasobami.

Jaka jest kolejność komend w typowym workflow Terraform? +

Standardowy workflow: (1) terraform init — pobiera provider plugins, inicjalizuje backend. (2) terraform validate — sprawdza składnię HCL. (3) terraform plan — pokazuje co zostanie zmienione (diff infrastruktury). (4) terraform apply — wykonuje zmiany po potwierdzeniu. (5) terraform destroy — usuwa wszystko. Zasada: zawsze sprawdzaj plan przed apply, szczególnie gdy widzisz "will be destroyed" — Terraform może chcieć odtworzyć zasób zamiast go zmodyfikować.

## Sprawdź oferty pasujące do tego scenariusza

Poniżej masz szybkie przejścia do ofert i stron z kodami rabatowymi tam, gdzie są dostępne.

Contabo

VPS z dobrą ceną do zarządzania przez Terraform — provider dostępny na Terraform Registry

VPS IaC

[Aktywuj rabat →](/out/contabo)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/contabo)

Mikrus

Tani VPS do nauki Terraform i testowania pipeline IaC

Tani VPS

[Aktywuj rabat →](/out/mikrus)

#Reklama · link partnerski

[Zobacz kod rabatowy →](/kody-rabatowe/mikrus)

## Powiązane strony

-   [Docker na VPS — instalacja i konfiguracja](/baza-wiedzy/docker-na-vps)
-   [Docker Compose w produkcji — best practices](/baza-wiedzy/docker-compose-produkcja)
-   [Grafana + Prometheus — monitoring VPS](/baza-wiedzy/monitoring-grafana-prometheus)
-   [Rsync — kopia zapasowa VPS](/baza-wiedzy/rsync-kopia-zapasowa-vps)
-   [Wszystkie artykuły](/baza-wiedzy/)