Terraform rootblockdevice controls the EBS volume that backs the root volume of an aws_instance. It is the declarative way to set volume type, size, IOPS, throughput, deleteontermination, and tags for the boot volume at creation time and to request in-place modifications afterwards. Understanding its interaction with the AWS provider, tag propagation, and the lifecycle of the instance is essential for stable EC2 automation.
Core rootblockdevice Attributes and Usage
The root_block_device nested block is defined inside the aws_instance resource. The block is evaluated at instance launch and influences the initial EBS volume parameters.
A minimal declaration includes volume type and size:
```hcl
resource "awsinstance" "ec2server1" {
ami = module.ami.ami1
instancetype = "t3.large"
subnetid = "az1"
key_name = "key1"
rootblockdevice {
volumetype = "gp3"
iops = 3000
throughput = 125
volumesize = 20
tags = {
"Name" = "server1"
"Environment" = "Sandbox"
"Project" = "adminprojet"
"Application" = "admintest"
"localapp" = "local1"
"external" = "external1"
}
}
tags = {
"Name" = "server1"
"Project" = "adminprojet"
"localapp" = "local1"
"external" = "external1"
"Application" = "admintest"
"Platform" = "Linux"
"Environment" = "Sandbox"
"Patching" = "yes"
"Power" = "24/7"
}
lifecycle {
ignorechanges = [ami, userdata, key_name]
}
}
```
In this configuration the root volume is created as gp3 with a baseline of 3000 IOPS and 125 MB/s throughput and a size of 20 GiB. The root_block_device block also carries its own tags map separate from the instance-level tags.
When using dynamic instance creation, the same pattern is repeated with variables:
```hcl
provider "aws" {
region = "us-east-1"
profile = "personal"
}
locals {
serverconfig = [
for srv in var.configuration : [
for i in range(1, srv.noofinstances+1) : {
instancename = "${srv.applicationname}-${i}"
instancetype = srv.instancetype
subnetid = srv.subnetid
ami = srv.ami
rootdisk = srv.rootblockdevice
blockdisks = srv.ebsblockdevices
securitygroupids = srv.vpcsecuritygroup_ids
}
]
]
}
locals {
instances = flatten(local.serverconfig)
}
resource "awsinstance" "web" {
foreach = { for server in local.instances : server.instance_name => server }
ami = each.value.ami
instancetype = each.value.instancetype
vpcsecuritygroupids = each.value.securitygroupids
keyname = "Sarav-Easy"
associatepublicipaddress = true
userdata = "${file("init.sh")}"
subnetid = each.value.subnetid
tags = {
Name = "${each.value.instance_name}"
}
rootblockdevice {
volumetype = each.value.rootdisk.volumetype
volumesize = each.value.rootdisk.volumesize
tags = each.value.rootdisk.tags
}
dynamic "ebsblockdevice" {
foreach = each.value.blockdisks
content {
volumetype = ebsblockdevice.value.volumetype
volumesize = ebsblockdevice.value.volumesize
tags = ebsblockdevice.value.tags
devicename = ebsblockdevice.value.device_name
}
}
}
```
The root_block_device block receives volume_type, volume_size and tags from each.value.rootdisk. A companion dynamic "ebs_block_device" block handles additional non-root EBS volumes per instance.
A tfvars example that feeds this pattern can define multiple machines with different EBS volumes:
hcl
configuration = [
{
"application_name" : "GritfyAppDev",
"ami" : "ami-0c02fb55956c7d316",
"no_of_instances" : "2",
"instance_type" : "t3a.nano",
"subnet_id" : "subnet-0f4f294d8404946eb",
"vpc_security_group_ids" : ["sg-0d15a4cac0567478c","sg-0d8749c35f7439f3e"],
"root_block_device": {
"volume_size": "30",
"volume_type": "gp3",
"tags": {
"FileSystem": "/root"
}
},
"ebs_block_devices": [
{
"device_name" :
}
]
}
]
The manifest processes the tfvars file and creates multiple EC2 instances with multiple EBS volumes and different configurations using for_each and count together.
Tag Drift on rootblockdevice
With Terraform CLI version v0.15.5 and provider registry.terraform.io/hashicorp/aws v3.38.0, a recurring plan behavior is observed on aws_instance rootblockdevice tags.
After the first terraform plan and terraform apply, the rootblockdevice volume is created correctly with all tags in the tfe file. A subsequent terraform plan shows all tags on rootblockdevice as new changes every time with a + sign, even though the tags were already applied to rootblockdevice by the first plan/apply.
Debug output illustrates the diff:
~ resource "aws_instance" "ec2_server1" {
id = "i-0ex5xsdsdsjhsisdb"
tags = {
"Name" = "server1"
"Project" = "admin_projet"
"local" = "local1"
"external" = "external1"
"Application" = "admin_test"
"Platform" = "Linux"
"Environment" = "Sandbox"
"Patching" = "yes"
"Power" = "24/7"
}
~ root_block_device {
~ tags = {
+ "Name" = "server1"
+ "Environment" = "Sandbox"
+ "Project" = "admin_projet"
+ "Application" = "admin_test"
+ "local" = "local1"
+ "external" = "external1"
}
(8 unchanged attributes hidden)
}
(5 unchanged blocks hidden)
}
Expected behavior is that if a tag was already applied to rootblockdevice terraform should not show this tag as new add tag on plan output for each plan. The actual behavior is that terraform plan output shows all tags on rootblockdevice as new changes with + sign on every run.
The configuration that triggers the observation contains both instance-level tags and rootblockdevice-level tags with overlapping keys:
- Instance tags include Name, Project, localapp, external, Application, Platform, Environment, Patching, Power.
- rootblockdevice tags include Name, Environment, Project, Application, localapp, external.
The plan repeatedly adds the rootblockdevice tags despite them being present in the state.
Steps to reproduce as reported:
- terraform plan; terraform apply : The rootblockdevice volume will be created correctly with all tags in tfe file.
- terraform plan again
The rootblockdevice volume already exists with tags, yet the next plan lists them as new.
In-Place Modification and Volume Resizing
The root volume is typically 8 GB by default, which fills up fast with applications, logs, Docker images, or data. You need to increase it without destroying and recreating the instance.
Adding or modifying root_block_device attributes on an existing aws_instance is supported as an in-place modification. Run terraform apply. Terraform modifies the EBS volume in place — no instance replacement needed.
The key word is in-place — not replaced. The EBS volume is larger, but the OS filesystem still sees the old size. A filesystem resize is required inside the OS after the volume size change.
Volume Type Comparison
| Type | IOPS | Throughput | Cost | Best For |
|---|---|---|---|---|
| gp3 | 3,000 baseline (up to 16,000) | 125 MB/s (up to 1,000) | $0.08/GB/mo | Most workloads ✅ |
| gp2 | 3 per GB (up to 16,000) | 250 MB/s | $0.10/GB/mo | Legacy (gp3 is better) |
| io2 | Up to 64,000 | 1,000 MB/s | $0.125/GB/mo + IOPS | Databases, high IOPS |
| st1 | 500 | 500 MB/s | $0.045/GB/mo | Throughput-heavy (not for root) |
Always use gp3 unless you have specific IOPS requirements. It's 20% cheaper than gp2 with better baseline performance.
The ModifyVolume API is the mechanism behind the in-place change. The EBS volume is larger, but the OS filesystem still sees the old size.
Testing Locally with LocalStack
Simulate AWS Services Locally with LocalStack and Terraform to test AWS infrastructure locally. Create S3 buckets, DynamoDB tables, and Lambda functions without AWS costs or credentials.
For EC2 root volume testing, LocalStack allows validation of root_block_device blocks and ebs_block_device mappings before applying to real AWS.
Practical Patterns
- Separate instance tags from rootblockdevice tags. Overlapping keys increase confusion during drift detection.
- Use variables for rootdisk and blockdisks to keep manifests DRY when creating multiple EC2 instances with different and multiple EBS volumes per EC2 instance.
- Keep
volume_typeconsistent with workload. gp3 is the default recommendation for most workloads. - When increasing size, set a new
volume_sizeinroot_block_device. Terraform will invoke the modify operation. - After a size increase, schedule OS-level growpart and resize2fs/xfs_growfs to make the OS see the new capacity.
Conclusion
Terraform rootblockdevice provides declarative control over the boot volume of an EC2 instance, including type, size, IOPS, throughput, and tags. In provider versions around v3.38.0 with Terraform v0.15.5, tag drift on rootblockdevice can manifest as perpetual additions on every plan, with the plan output showing all tags as new changes despite them already existing on the volume. This behavior is specific to the rootblockdevice nested block and differs from instance-level tag handling.
In-place modification is supported for volume type, size, IOPS and throughput changes without instance replacement, enabling safe growth of the root volume for applications, logs and Docker images. The volume type choice matters for cost and performance, with gp3 offering a lower cost per GB per month than gp2 and a better baseline. After a successful modify, the filesystem must be expanded inside the OS to utilize the new capacity.
Using for_each with a flattened server config and dynamic ebs_block_device blocks allows multiple EC2 instances with multiple EBS volumes per instance to be created from a single tfvars file. Testing these patterns locally with LocalStack reduces risk before real AWS applies.