Terraform Provider Overview
The official DanubeData Terraform provider allows you to manage your cloud infrastructure using Infrastructure as Code (IaC). Deploy VPS instances, databases, caches, storage buckets, and more with version-controlled, reproducible configurations.
Why Use Terraform?
- Version Control: Track infrastructure changes in Git alongside your application code
- Reproducible Deployments: Deploy identical environments across dev, staging, and production
- Team Collaboration: Share configurations and collaborate with pull requests
- Drift Detection: Detect and fix configuration drift automatically
- Cost Visibility: Preview costs before deployment with
terraform plan
Quick Start
1. Install Terraform
Download and install Terraform from terraform.io:
# macOS with Homebrew
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
# 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
# Verify installation
terraform version
2. Get Your API Token
- Log in to your DanubeData dashboard
- Navigate to Security > API tokens
- Click Create token
- Select the permissions you need (at minimum: read/write for resources you want to manage)
- Copy the token - it's shown only once!
3. Configure the Provider
Create a new directory for your Terraform configuration:
mkdir my-infrastructure
cd my-infrastructure
Create a main.tf file:
terraform {
required_providers {
danubedata = {
source = "AdrianSilaghi/danubedata"
version = "~> 0.4"
}
}
}
provider "danubedata" {
# API token is read from DANUBEDATA_API_TOKEN environment variable
}
Set your API token as an environment variable:
export DANUBEDATA_API_TOKEN="your-api-token-here"
4. Initialize and Apply
# Initialize the provider
terraform init
# Preview changes
terraform plan
# Apply changes
terraform apply
Provider Configuration
The provider supports the following configuration options:
| Attribute | Type | Required | Description |
|---|---|---|---|
api_token | string | Yes | Your DanubeData API token. Can also be set via DANUBEDATA_API_TOKEN environment variable. |
base_url | string | No | API endpoint URL. Defaults to https://danubedata.ro/api/v1. Can be set via DANUBEDATA_BASE_URL environment variable. |
team_id | number | No | The ID of the project to work in. Needs provider 0.4.0 or later. Can also be set via DANUBEDATA_TEAM_ID environment variable. See Selecting a project. |
Authentication Methods
Recommended: Environment Variable
export DANUBEDATA_API_TOKEN="your-api-token"
provider "danubedata" {}
Alternative: Direct Configuration (not recommended for production)
provider "danubedata" {
api_token = "your-api-token" # Don't commit this to version control!
}
Using Terraform Variables
variable "danubedata_token" {
type = string
sensitive = true
}
provider "danubedata" {
api_token = var.danubedata_token
}
Then set the variable:
export TF_VAR_danubedata_token="your-api-token"
Selecting a project
Every resource belongs to one project, and one provider block works in one project. Set team_id to choose the project, or set the DANUBEDATA_TEAM_ID environment variable. Both take the project's ID, a whole number, and need provider 0.4.0 or later. The API calls a project a team, which is why the setting is named team_id.
provider "danubedata" {
team_id = 123
}
The provider sends the ID with every request. If you leave it out, the API picks the project from your token:
- A token made with This project only works in that project. If you set
team_idto a different project, the API refuses the request with403 Forbidden. - A token made with All your projects works in your current project, the one you last switched to in the console.
Set
team_idif your token can reach more than one project. Your current project changes each time you switch project in the console, so the same configuration can end up in a different project. From 19 November 2026 a request works in one project only, and a resource in any other project answers404 Not Found. Terraform reads that as "deleted outside Terraform": it removes the resource from its state and plans to create it again. Applying that plan makes a second copy, which is billed, while the original keeps running in its own project.
Working with more than one project
One provider block cannot switch projects. Add one block for each project, give each an alias, and name the block you want with provider on every resource and data source:
provider "danubedata" {
alias = "production"
team_id = 123
}
provider "danubedata" {
alias = "staging"
team_id = 456
}
resource "danubedata_database" "production" {
provider = danubedata.production
name = "app-database"
engine = "postgresql"
version = "16"
resource_profile = "small"
datacenter = "fsn1"
}
resource "danubedata_database" "staging" {
provider = danubedata.staging
name = "app-database"
engine = "postgresql"
version = "16"
resource_profile = "small"
datacenter = "fsn1"
}
Each block needs a token that can reach its project. One token made with All your projects covers every block. Or set api_token in each block to a token made with This project only for that project.
Finding a project's ID
Run danube project ls with the Danube CLI. It prints each project's name and ID. You can also ask the API with any API token:
curl -H "Authorization: Bearer $DANUBEDATA_API_TOKEN" https://danubedata.ro/api/v1/user/teams
The answer lists your projects with their id and name. current_team_id is the project the token works in when no ID is sent, so it shows where Terraform would act without team_id.
Available Resources
The provider includes the following resource types:
| Resource | Description |
|---|---|
danubedata_vps | Virtual machines with shared or dedicated CPU |
danubedata_database | Managed MySQL, PostgreSQL, and MariaDB databases |
danubedata_database_replica | Read replicas for a managed database |
danubedata_cache | Redis, Valkey, and Dragonfly cache instances |
danubedata_parameter_group | Reusable database/cache configuration presets |
danubedata_serverless | Scale-to-zero serverless containers |
danubedata_static_site | Static site hosting |
danubedata_static_site_domain | Custom domain for a static site |
danubedata_storage_bucket | S3-compatible object storage buckets |
danubedata_storage_access_key | Access keys for object storage |
danubedata_ssh_key | SSH keys for VPS authentication |
danubedata_firewall | Network security rules |
danubedata_vps_snapshot | Point-in-time VPS backups |
danubedata_database_snapshot | Point-in-time database backups |
danubedata_cache_snapshot | Point-in-time cache backups |
See Terraform Resources for detailed documentation.
Available Data Sources
The provider includes these data sources for querying existing resources:
| Data Source | Description |
|---|---|
danubedata_vpss | List existing VPS instances |
danubedata_vps_images | List available OS images |
danubedata_databases | List existing databases |
danubedata_database_providers | List available database engines |
danubedata_database_snapshots | List database snapshots |
danubedata_caches | List existing cache instances |
danubedata_cache_providers | List available cache providers |
danubedata_cache_snapshots | List cache snapshots |
danubedata_parameter_groups | List parameter groups |
danubedata_serverless_containers | List serverless containers |
danubedata_static_sites | List static sites |
danubedata_storage_buckets | List storage buckets |
danubedata_storage_access_keys | List storage access keys |
danubedata_ssh_keys | List existing SSH keys |
danubedata_firewalls | List firewalls |
danubedata_vps_snapshots | List VPS snapshots |
See Terraform Data Sources for detailed documentation.
Supported Datacenters
All resources are deployed to our single datacenter:
| Code | Location | Country |
|---|---|---|
fsn1 | Falkenstein | Germany |
State Management
Terraform tracks your infrastructure state in a state file. The state — not your API token — records which resources Terraform already manages, so it is what decides whether a plan creates, changes or destroys something. For team collaboration, use a remote backend.
Using DanubeData Object Storage as Backend
terraform {
backend "s3" {
bucket = "terraform-state"
key = "my-project/terraform.tfstate"
region = "fsn1"
endpoints = { s3 = "https://s3.danubedata.ro" }
use_path_style = true
use_lockfile = true
skip_credentials_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
skip_region_validation = true
skip_s3_checksum = true
}
}
The skip_* flags are required: our object storage is S3-compatible but is not AWS, so the AWS-specific credential, account-ID and metadata lookups have to be turned off. use_lockfile = true enables state locking against the bucket itself, with no DynamoDB table, so two CI runs cannot write the state at once — it needs Terraform 1.10 or later.
Set S3 credentials from a storage access key:
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
Importing Existing Resources
You can import existing DanubeData resources into Terraform management:
# Import a VPS instance
terraform import danubedata_vps.my_server 12345
# Import a database
terraform import danubedata_database.my_db 67890
# Import a storage bucket
terraform import danubedata_storage_bucket.my_bucket abc123
Find resource IDs in the DanubeData dashboard or via the API.
Best Practices
Give Each Environment Its Own State
Separate environments need separate state. The API token and team_id decide which project a call goes to, but they do not change what Terraform already believes it manages — that comes from the state file.
Do not point one root module at several environments by changing a variable. If your state already holds a
productionresource and you re-run it withenvironment = "test", Terraform does not create something new: it compares the config against the resource it already tracks and plans to replace it. Attributes such asname,engineanddatabase_namecannot be changed on a running instance, so the plan comes back as1 to add, 1 to destroy— and the resource it destroys is the live production one.
Give each environment its own root module, its own state key and its own token:
modules/stack/ shared resource definitions, taking env as a variable
envs/prod/ key prod/terraform.tfstate, production token
envs/test/ key test/terraform.tfstate, test token
Each environment directory holds only a backend block, a provider block and a call to the shared module:
# envs/test/main.tf
terraform {
backend "s3" {
bucket = "my-tfstate"
key = "test/terraform.tfstate"
# ...remaining backend settings as above
}
}
provider "danubedata" {} # token from DANUBEDATA_API_TOKEN
module "stack" {
source = "../../modules/stack"
env = "test"
}
Now terraform apply in envs/test/ starts from an empty state and creates the test resources, while production is untouched — because a different state file describes it.
Resource names are unique per project rather than globally, so the same name can be used in every environment and only values that must differ, such as a database's initial database name, need to include var.env.
Terraform workspaces are an alternative, but the API token still has to change per environment through the shell environment, so separate root modules are harder to get wrong.
Use One Project per Environment
Create a separate project for each environment. Point each environment's provider block at its project with team_id, and give it its own API token made with This project only. Projects are the isolation boundary on our side: separate Kubernetes namespace, separate quotas and separate billing lines.
# envs/prod/main.tf
provider "danubedata" {
team_id = 123 # the production project
}
A token locked to its project cannot reach any other. If team_id names the wrong project, the API refuses the request instead of acting there. See Selecting a project.
Mark Sensitive Outputs
output "database_password" {
value = danubedata_database.main.password
sensitive = true
}
Use Terraform Cloud for Team Collaboration
For larger teams, consider using Terraform Cloud for:
- Remote state management
- Policy enforcement
- VCS integration
- Cost estimation
Troubleshooting
Authentication Errors
If you see "401 Unauthorized":
- Verify your API token is correct
- Check the token has the required permissions
- Ensure the
DANUBEDATA_API_TOKENenvironment variable is set
Provider Not Found
If Terraform can't find the provider:
terraform init -upgrade
Resource Timeouts
For long-running operations, configure timeouts:
resource "danubedata_vps" "app" {
# ...
timeouts {
create = "10m"
update = "10m"
delete = "5m"
}
}
Next Steps
- Terraform Resources - Detailed resource documentation
- Terraform Data Sources - Query existing resources
- Terraform Examples - Real-world configuration examples
- Terraform Registry - Official provider documentation
Support
- Documentation: registry.terraform.io/providers/AdrianSilaghi/danubedata
- GitHub Issues: Report bugs and request features
- Contact Support: Reach out to our support team for help