The gcp:compute/address:Address resource, part of the Pulumi GCP provider, reserves static IP addresses that persist independently of VM instances. This guide focuses on four capabilities: external IP reservation, internal IP allocation within subnets, VM instance attachment, and IPsec Interconnect IP ranges.
Addresses reference VPC networks and subnets that must exist separately. Internal addresses require explicit addressType and subnet configuration. The examples are intentionally small. Combine them with your own VPC infrastructure and VM configurations.
Reserve an external IP address
Most deployments start by reserving a static external IP that persists beyond individual VM lifecycles.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const ipAddress = new gcp.compute.Address("ip_address", {name: "my-address"});
import pulumi
import pulumi_gcp as gcp
ip_address = gcp.compute.Address("ip_address", name="my-address")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := compute.NewAddress(ctx, "ip_address", &compute.AddressArgs{
Name: pulumi.String("my-address"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var ipAddress = new Gcp.Compute.Address("ip_address", new()
{
Name = "my-address",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Address;
import com.pulumi.gcp.compute.AddressArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var ipAddress = new Address("ipAddress", AddressArgs.builder()
.name("my-address")
.build());
}
}
resources:
ipAddress:
type: gcp:compute:Address
name: ip_address
properties:
name: my-address
When addressType is omitted, the address defaults to EXTERNAL. The reserved IP can be attached to VM instances, load balancers, or other resources that need stable external connectivity. The name must comply with RFC1035 naming conventions.
Reserve an internal IP within a subnet
Applications communicating within a VPC often need static internal IPs to maintain stable connectivity between services.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const _default = new gcp.compute.Network("default", {name: "my-network"});
const defaultSubnetwork = new gcp.compute.Subnetwork("default", {
name: "my-subnet",
ipCidrRange: "10.0.0.0/16",
region: "us-central1",
network: _default.id,
});
const internalWithSubnetAndAddress = new gcp.compute.Address("internal_with_subnet_and_address", {
name: "my-internal-address",
subnetwork: defaultSubnetwork.id,
addressType: "INTERNAL",
address: "10.0.42.42",
region: "us-central1",
});
import pulumi
import pulumi_gcp as gcp
default = gcp.compute.Network("default", name="my-network")
default_subnetwork = gcp.compute.Subnetwork("default",
name="my-subnet",
ip_cidr_range="10.0.0.0/16",
region="us-central1",
network=default.id)
internal_with_subnet_and_address = gcp.compute.Address("internal_with_subnet_and_address",
name="my-internal-address",
subnetwork=default_subnetwork.id,
address_type="INTERNAL",
address="10.0.42.42",
region="us-central1")
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_default, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
Name: pulumi.String("my-network"),
})
if err != nil {
return err
}
defaultSubnetwork, err := compute.NewSubnetwork(ctx, "default", &compute.SubnetworkArgs{
Name: pulumi.String("my-subnet"),
IpCidrRange: pulumi.String("10.0.0.0/16"),
Region: pulumi.String("us-central1"),
Network: _default.ID(),
})
if err != nil {
return err
}
_, err = compute.NewAddress(ctx, "internal_with_subnet_and_address", &compute.AddressArgs{
Name: pulumi.String("my-internal-address"),
Subnetwork: defaultSubnetwork.ID(),
AddressType: pulumi.String("INTERNAL"),
Address: pulumi.String("10.0.42.42"),
Region: pulumi.String("us-central1"),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var @default = new Gcp.Compute.Network("default", new()
{
Name = "my-network",
});
var defaultSubnetwork = new Gcp.Compute.Subnetwork("default", new()
{
Name = "my-subnet",
IpCidrRange = "10.0.0.0/16",
Region = "us-central1",
Network = @default.Id,
});
var internalWithSubnetAndAddress = new Gcp.Compute.Address("internal_with_subnet_and_address", new()
{
Name = "my-internal-address",
Subnetwork = defaultSubnetwork.Id,
AddressType = "INTERNAL",
IPAddress = "10.0.42.42",
Region = "us-central1",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.Subnetwork;
import com.pulumi.gcp.compute.SubnetworkArgs;
import com.pulumi.gcp.compute.Address;
import com.pulumi.gcp.compute.AddressArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var default_ = new Network("default", NetworkArgs.builder()
.name("my-network")
.build());
var defaultSubnetwork = new Subnetwork("defaultSubnetwork", SubnetworkArgs.builder()
.name("my-subnet")
.ipCidrRange("10.0.0.0/16")
.region("us-central1")
.network(default_.id())
.build());
var internalWithSubnetAndAddress = new Address("internalWithSubnetAndAddress", AddressArgs.builder()
.name("my-internal-address")
.subnetwork(defaultSubnetwork.id())
.addressType("INTERNAL")
.address("10.0.42.42")
.region("us-central1")
.build());
}
}
resources:
default:
type: gcp:compute:Network
properties:
name: my-network
defaultSubnetwork:
type: gcp:compute:Subnetwork
name: default
properties:
name: my-subnet
ipCidrRange: 10.0.0.0/16
region: us-central1
network: ${default.id}
internalWithSubnetAndAddress:
type: gcp:compute:Address
name: internal_with_subnet_and_address
properties:
name: my-internal-address
subnetwork: ${defaultSubnetwork.id}
addressType: INTERNAL
address: 10.0.42.42
region: us-central1
Setting addressType to INTERNAL reserves an IP within the VPC’s private address space. The address property specifies the exact IP (must fall within the subnet’s CIDR range), and subnetwork ties the reservation to a specific subnet. The region must match the subnet’s region.
Attach a static IP to a VM instance
VM instances serving public traffic or requiring consistent DNS records need static external IPs.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const static = new gcp.compute.Address("static", {name: "ipv4-address"});
const debianImage = gcp.compute.getImage({
family: "debian-11",
project: "debian-cloud",
});
const instanceWithIp = new gcp.compute.Instance("instance_with_ip", {
name: "vm-instance",
machineType: "f1-micro",
zone: "us-central1-a",
bootDisk: {
initializeParams: {
image: debianImage.then(debianImage => debianImage.selfLink),
},
},
networkInterfaces: [{
network: "default",
accessConfigs: [{
natIp: static.address,
}],
}],
});
import pulumi
import pulumi_gcp as gcp
static = gcp.compute.Address("static", name="ipv4-address")
debian_image = gcp.compute.get_image(family="debian-11",
project="debian-cloud")
instance_with_ip = gcp.compute.Instance("instance_with_ip",
name="vm-instance",
machine_type="f1-micro",
zone="us-central1-a",
boot_disk={
"initialize_params": {
"image": debian_image.self_link,
},
},
network_interfaces=[{
"network": "default",
"access_configs": [{
"nat_ip": static.address,
}],
}])
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
static, err := compute.NewAddress(ctx, "static", &compute.AddressArgs{
Name: pulumi.String("ipv4-address"),
})
if err != nil {
return err
}
debianImage, err := compute.LookupImage(ctx, &compute.LookupImageArgs{
Family: pulumi.StringRef("debian-11"),
Project: pulumi.StringRef("debian-cloud"),
}, nil)
if err != nil {
return err
}
_, err = compute.NewInstance(ctx, "instance_with_ip", &compute.InstanceArgs{
Name: pulumi.String("vm-instance"),
MachineType: pulumi.String("f1-micro"),
Zone: pulumi.String("us-central1-a"),
BootDisk: &compute.InstanceBootDiskArgs{
InitializeParams: &compute.InstanceBootDiskInitializeParamsArgs{
Image: pulumi.String(debianImage.SelfLink),
},
},
NetworkInterfaces: compute.InstanceNetworkInterfaceArray{
&compute.InstanceNetworkInterfaceArgs{
Network: pulumi.String("default"),
AccessConfigs: compute.InstanceNetworkInterfaceAccessConfigArray{
&compute.InstanceNetworkInterfaceAccessConfigArgs{
NatIp: static.Address,
},
},
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var @static = new Gcp.Compute.Address("static", new()
{
Name = "ipv4-address",
});
var debianImage = Gcp.Compute.GetImage.Invoke(new()
{
Family = "debian-11",
Project = "debian-cloud",
});
var instanceWithIp = new Gcp.Compute.Instance("instance_with_ip", new()
{
Name = "vm-instance",
MachineType = "f1-micro",
Zone = "us-central1-a",
BootDisk = new Gcp.Compute.Inputs.InstanceBootDiskArgs
{
InitializeParams = new Gcp.Compute.Inputs.InstanceBootDiskInitializeParamsArgs
{
Image = debianImage.Apply(getImageResult => getImageResult.SelfLink),
},
},
NetworkInterfaces = new[]
{
new Gcp.Compute.Inputs.InstanceNetworkInterfaceArgs
{
Network = "default",
AccessConfigs = new[]
{
new Gcp.Compute.Inputs.InstanceNetworkInterfaceAccessConfigArgs
{
NatIp = @static.IPAddress,
},
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Address;
import com.pulumi.gcp.compute.AddressArgs;
import com.pulumi.gcp.compute.ComputeFunctions;
import com.pulumi.gcp.compute.inputs.GetImageArgs;
import com.pulumi.gcp.compute.Instance;
import com.pulumi.gcp.compute.InstanceArgs;
import com.pulumi.gcp.compute.inputs.InstanceBootDiskArgs;
import com.pulumi.gcp.compute.inputs.InstanceBootDiskInitializeParamsArgs;
import com.pulumi.gcp.compute.inputs.InstanceNetworkInterfaceArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var static_ = new Address("static", AddressArgs.builder()
.name("ipv4-address")
.build());
final var debianImage = ComputeFunctions.getImage(GetImageArgs.builder()
.family("debian-11")
.project("debian-cloud")
.build());
var instanceWithIp = new Instance("instanceWithIp", InstanceArgs.builder()
.name("vm-instance")
.machineType("f1-micro")
.zone("us-central1-a")
.bootDisk(InstanceBootDiskArgs.builder()
.initializeParams(InstanceBootDiskInitializeParamsArgs.builder()
.image(debianImage.selfLink())
.build())
.build())
.networkInterfaces(InstanceNetworkInterfaceArgs.builder()
.network("default")
.accessConfigs(InstanceNetworkInterfaceAccessConfigArgs.builder()
.natIp(static_.address())
.build())
.build())
.build());
}
}
resources:
static:
type: gcp:compute:Address
properties:
name: ipv4-address
instanceWithIp:
type: gcp:compute:Instance
name: instance_with_ip
properties:
name: vm-instance
machineType: f1-micro
zone: us-central1-a
bootDisk:
initializeParams:
image: ${debianImage.selfLink}
networkInterfaces:
- network: default
accessConfigs:
- natIp: ${static.address}
variables:
debianImage:
fn::invoke:
function: gcp:compute:getImage
arguments:
family: debian-11
project: debian-cloud
The VM’s accessConfigs block references the reserved address via natIp. This attaches the static external IP to the instance’s network interface, ensuring the IP persists across instance restarts or replacements.
Reserve an IP range for IPsec Interconnect
HA VPN over Cloud Interconnect configurations require reserved IP ranges for VLAN attachments.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const network = new gcp.compute.Network("network", {
name: "test-network",
autoCreateSubnetworks: false,
});
const ipsec_interconnect_address = new gcp.compute.Address("ipsec-interconnect-address", {
name: "test-address",
addressType: "INTERNAL",
purpose: "IPSEC_INTERCONNECT",
address: "192.168.1.0",
prefixLength: 29,
network: network.selfLink,
});
import pulumi
import pulumi_gcp as gcp
network = gcp.compute.Network("network",
name="test-network",
auto_create_subnetworks=False)
ipsec_interconnect_address = gcp.compute.Address("ipsec-interconnect-address",
name="test-address",
address_type="INTERNAL",
purpose="IPSEC_INTERCONNECT",
address="192.168.1.0",
prefix_length=29,
network=network.self_link)
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
network, err := compute.NewNetwork(ctx, "network", &compute.NetworkArgs{
Name: pulumi.String("test-network"),
AutoCreateSubnetworks: pulumi.Bool(false),
})
if err != nil {
return err
}
_, err = compute.NewAddress(ctx, "ipsec-interconnect-address", &compute.AddressArgs{
Name: pulumi.String("test-address"),
AddressType: pulumi.String("INTERNAL"),
Purpose: pulumi.String("IPSEC_INTERCONNECT"),
Address: pulumi.String("192.168.1.0"),
PrefixLength: pulumi.Int(29),
Network: network.SelfLink,
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;
return await Deployment.RunAsync(() =>
{
var network = new Gcp.Compute.Network("network", new()
{
Name = "test-network",
AutoCreateSubnetworks = false,
});
var ipsec_interconnect_address = new Gcp.Compute.Address("ipsec-interconnect-address", new()
{
Name = "test-address",
AddressType = "INTERNAL",
Purpose = "IPSEC_INTERCONNECT",
IPAddress = "192.168.1.0",
PrefixLength = 29,
Network = network.SelfLink,
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.Address;
import com.pulumi.gcp.compute.AddressArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
public class App {
public static void main(String[] args) {
Pulumi.run(App::stack);
}
public static void stack(Context ctx) {
var network = new Network("network", NetworkArgs.builder()
.name("test-network")
.autoCreateSubnetworks(false)
.build());
var ipsec_interconnect_address = new Address("ipsec-interconnect-address", AddressArgs.builder()
.name("test-address")
.addressType("INTERNAL")
.purpose("IPSEC_INTERCONNECT")
.address("192.168.1.0")
.prefixLength(29)
.network(network.selfLink())
.build());
}
}
resources:
ipsec-interconnect-address:
type: gcp:compute:Address
properties:
name: test-address
addressType: INTERNAL
purpose: IPSEC_INTERCONNECT
address: 192.168.1.0
prefixLength: 29
network: ${network.selfLink}
network:
type: gcp:compute:Network
properties:
name: test-network
autoCreateSubnetworks: false
Setting purpose to IPSEC_INTERCONNECT reserves an IP range (not a single address) for VPN connectivity. The prefixLength defines the range size (29 provides 8 addresses), and network ties the reservation to a specific VPC. This configuration is required for HA VPN over Cloud Interconnect.
Beyond these examples
These snippets focus on specific address-level features: external and internal IP reservation, subnet-specific addressing, and VM instance attachment and IPsec Interconnect ranges. They’re intentionally minimal rather than full networking deployments.
The examples may reference pre-existing infrastructure such as VPC networks and subnets, and VM images from public projects. They focus on configuring the address rather than provisioning everything around it.
To keep things focused, common address patterns are omitted, including:
- Network tier selection (networkTier for PREMIUM vs STANDARD)
- IPv6 addressing (ipVersion, ipv6EndpointType)
- Shared load balancer VIPs (purpose: SHARED_LOADBALANCER_VIP)
- VPC peering addresses (purpose: VPC_PEERING)
- Labels and metadata (labels property)
These omissions are intentional: the goal is to illustrate how each address feature is wired, not provide drop-in networking modules. See the Compute Address resource reference for all available configuration options.
Let's reserve and Manage GCP IP Addresses
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Common Pitfalls & Limitations
networkTier must be left unset when addressType is INTERNAL. Setting it causes configuration errors.labels field is non-authoritative and only manages labels in your configuration. Use effectiveLabels to see all labels present on the resource, including those set by other clients.Address Types & Configuration
EXTERNAL addresses (default) are public IPs for internet communication. INTERNAL addresses are private IPs within your VPC for internal communication between resources.Purpose is only for INTERNAL addresses:
GCE_ENDPOINT- VM instances, alias IPs, load balancers (most common)SHARED_LOADBALANCER_VIP- Multiple internal load balancersVPC_PEERING- VPC peer networksIPSEC_INTERCONNECT- HA VPN over Cloud InterconnectPRIVATE_SERVICE_CONNECT- Private Service Connect (global only)
[a-z][-a-z0-9]*[a-z0-9]?).IP Address Assignment
address property to your desired IP. For internal addresses, it must be within the subnetwork’s IP range (e.g., address: "10.0.42.42").address property unset and GCP will automatically assign an available IP address.Usage & Integration
addressType to INTERNAL, specify the subnetwork ID, and optionally provide a specific address within the subnet’s range.networkInterfaces configuration using accessConfigs.natIp set to the address’s address output property.Using a different cloud?
Explore networking guides for other cloud providers: