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 three capabilities: external IP reservation for public endpoints, internal IP allocation within subnets, and purpose-based addressing for specialized use cases.
Addresses reference VPC networks and subnets that must exist separately. VM instances attach addresses through network interface configuration. The examples are intentionally small. Combine them with your own VPC infrastructure and instance definitions.
Reserve an external IP address
Most deployments start by reserving a static external IP that persists beyond individual VM lifecycles, providing a stable endpoint for DNS and external clients.
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, it defaults to EXTERNAL. The name property must follow RFC1035 naming rules. The address value is assigned by GCP if not specified, and the region defaults to your provider configuration.
Reserve an internal IP within a subnet
Applications communicating within a VPC often need predictable internal addresses for databases, internal APIs, or service discovery.
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 and specifying a subnetwork pins the address within that subnet’s CIDR range. The address property must fall within the subnet’s IP range (here, 10.0.42.42 within 10.0.0.0/16). The region must match the subnet’s region.
Reserve an internal address for VM endpoints
VM instances and load balancers require internal addresses with the GCE_ENDPOINT purpose for proper VPC routing.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const internalWithGceEndpoint = new gcp.compute.Address("internal_with_gce_endpoint", {
name: "my-internal-address-",
addressType: "INTERNAL",
purpose: "GCE_ENDPOINT",
});
import pulumi
import pulumi_gcp as gcp
internal_with_gce_endpoint = gcp.compute.Address("internal_with_gce_endpoint",
name="my-internal-address-",
address_type="INTERNAL",
purpose="GCE_ENDPOINT")
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, "internal_with_gce_endpoint", &compute.AddressArgs{
Name: pulumi.String("my-internal-address-"),
AddressType: pulumi.String("INTERNAL"),
Purpose: pulumi.String("GCE_ENDPOINT"),
})
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 internalWithGceEndpoint = new Gcp.Compute.Address("internal_with_gce_endpoint", new()
{
Name = "my-internal-address-",
AddressType = "INTERNAL",
Purpose = "GCE_ENDPOINT",
});
});
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 internalWithGceEndpoint = new Address("internalWithGceEndpoint", AddressArgs.builder()
.name("my-internal-address-")
.addressType("INTERNAL")
.purpose("GCE_ENDPOINT")
.build());
}
}
resources:
internalWithGceEndpoint:
type: gcp:compute:Address
name: internal_with_gce_endpoint
properties:
name: my-internal-address-
addressType: INTERNAL
purpose: GCE_ENDPOINT
The purpose property controls how the address is used. GCE_ENDPOINT indicates the address will be attached to VM instances, alias IP ranges, or load balancers. When purpose is set, addressType must be INTERNAL.
Attach a static IP to a VM instance
VM instances serving public traffic need stable external IPs that survive instance recreation.
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 networkInterfaces block references the reserved address through accessConfigs. The natIp property points to the address resource’s address output property, which contains the actual IP value assigned by GCP.
Reserve an IP range for IPsec interconnect
HA VPN over Cloud Interconnect requires reserved IP ranges with specific prefix lengths 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
The IPSEC_INTERCONNECT purpose reserves an IP range rather than a single address. The prefixLength property defines the range size (29 provides 8 addresses). The network property specifies which VPC network owns the range, and the address property sets the range’s starting IP.
Beyond these examples
These snippets focus on specific address-level features: external and internal IP reservation, subnet-specific and network-wide addressing, and purpose-based allocation. They’re intentionally minimal rather than full networking deployments.
The examples may reference pre-existing infrastructure such as VPC networks and subnets, and VM instances for IP attachment. 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)
- VPC peering and shared load balancer addresses (VPC_PEERING, SHARED_LOADBALANCER_VIP purposes)
- 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
Address Types & Configuration
purpose value and should not have networkTier set, as internal traffic is always Premium tier.Purpose values are only for INTERNAL addresses:
GCE_ENDPOINTfor VM instances, alias IPs, and load balancersSHARED_LOADBALANCER_VIPfor addresses shared by multiple internal load balancersVPC_PEERINGfor VPC peer network addressesIPSEC_INTERCONNECTfor HA VPN over Cloud InterconnectPRIVATE_SERVICE_CONNECTfor Private Service Connect (global internal only)
labels can be modified. All other properties (including address, name, addressType, region, subnetwork, purpose, and networkTier) are immutable and require recreating the resource.Network Configuration
networkTier unset when addressType is INTERNAL.subnetwork for INTERNAL addresses with GCE_ENDPOINT or DNS_RESOLVER purposes. Use network for INTERNAL addresses with VPC_PEERING or IPSEC_INTERCONNECT purposes. External addresses don’t require either.IP Address Management
addressType to INTERNAL, specify the subnetwork, and provide your desired IP in the address field. The IP must be within the subnetwork’s IP range.address output in your VM’s networkInterfaces.accessConfigs.natIp field.Resource Management
labels field is non-authoritative and only manages labels in your configuration. Use effectiveLabels to see all labels on the resource, including those set by other clients or services.Using a different cloud?
Explore networking guides for other cloud providers: