The aws:lightsail/containerService:ContainerService resource, part of the Pulumi AWS provider, provisions the compute and networking platform for running containerized applications in Lightsail. This guide focuses on three capabilities: compute capacity configuration, custom domain mapping, and private ECR registry access.
Container services require SSL/TLS certificates for custom domains and ECR repositories for private images. The examples are intentionally small. Combine them with your own container deployment configurations, which define images, ports, and environment variables separately from the service resource.
Create a container service with compute capacity
Most deployments start by defining compute capacity through power and scale settings, which determine the memory, vCPUs, and number of nodes available to your containers.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.lightsail.ContainerService("example", {
name: "container-service-1",
power: "nano",
scale: 1,
isDisabled: false,
tags: {
foo1: "bar1",
foo2: "",
},
});
import pulumi
import pulumi_aws as aws
example = aws.lightsail.ContainerService("example",
name="container-service-1",
power="nano",
scale=1,
is_disabled=False,
tags={
"foo1": "bar1",
"foo2": "",
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lightsail"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := lightsail.NewContainerService(ctx, "example", &lightsail.ContainerServiceArgs{
Name: pulumi.String("container-service-1"),
Power: pulumi.String("nano"),
Scale: pulumi.Int(1),
IsDisabled: pulumi.Bool(false),
Tags: pulumi.StringMap{
"foo1": pulumi.String("bar1"),
"foo2": pulumi.String(""),
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.LightSail.ContainerService("example", new()
{
Name = "container-service-1",
Power = "nano",
Scale = 1,
IsDisabled = false,
Tags =
{
{ "foo1", "bar1" },
{ "foo2", "" },
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lightsail.ContainerService;
import com.pulumi.aws.lightsail.ContainerServiceArgs;
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 example = new ContainerService("example", ContainerServiceArgs.builder()
.name("container-service-1")
.power("nano")
.scale(1)
.isDisabled(false)
.tags(Map.ofEntries(
Map.entry("foo1", "bar1"),
Map.entry("foo2", "")
))
.build());
}
}
resources:
example:
type: aws:lightsail:ContainerService
properties:
name: container-service-1
power: nano
scale: 1
isDisabled: false
tags:
foo1: bar1
foo2: ""
The power property selects a node size (nano through xlarge), controlling memory and vCPU allocation per node. The scale property sets how many nodes run in parallel. Together, these determine your service’s total compute capacity and monthly cost. The isDisabled property lets you pause the service without deleting it.
Map custom domains to the container service
To serve traffic on custom domains like www.example.com, you associate those domains with the service before deploying containers.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const example = new aws.lightsail.ContainerService("example", {publicDomainNames: {
certificates: [{
certificateName: "example-certificate",
domainNames: ["www.example.com"],
}],
}});
import pulumi
import pulumi_aws as aws
example = aws.lightsail.ContainerService("example", public_domain_names={
"certificates": [{
"certificate_name": "example-certificate",
"domain_names": ["www.example.com"],
}],
})
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lightsail"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
_, err := lightsail.NewContainerService(ctx, "example", &lightsail.ContainerServiceArgs{
PublicDomainNames: &lightsail.ContainerServicePublicDomainNamesArgs{
Certificates: lightsail.ContainerServicePublicDomainNamesCertificateArray{
&lightsail.ContainerServicePublicDomainNamesCertificateArgs{
CertificateName: pulumi.String("example-certificate"),
DomainNames: pulumi.StringArray{
pulumi.String("www.example.com"),
},
},
},
},
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var example = new Aws.LightSail.ContainerService("example", new()
{
PublicDomainNames = new Aws.LightSail.Inputs.ContainerServicePublicDomainNamesArgs
{
Certificates = new[]
{
new Aws.LightSail.Inputs.ContainerServicePublicDomainNamesCertificateArgs
{
CertificateName = "example-certificate",
DomainNames = new[]
{
"www.example.com",
},
},
},
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lightsail.ContainerService;
import com.pulumi.aws.lightsail.ContainerServiceArgs;
import com.pulumi.aws.lightsail.inputs.ContainerServicePublicDomainNamesArgs;
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 example = new ContainerService("example", ContainerServiceArgs.builder()
.publicDomainNames(ContainerServicePublicDomainNamesArgs.builder()
.certificates(ContainerServicePublicDomainNamesCertificateArgs.builder()
.certificateName("example-certificate")
.domainNames("www.example.com")
.build())
.build())
.build());
}
}
resources:
example:
type: aws:lightsail:ContainerService
properties:
publicDomainNames:
certificates:
- certificateName: example-certificate
domainNames:
- www.example.com
The publicDomainNames property maps SSL/TLS certificates to domain names. You must create and validate the certificate in Lightsail before referencing it here. When you deploy a container with a public endpoint, Lightsail routes traffic from these domains to your application.
Pull images from Amazon ECR private repositories
Container services that use private ECR images need IAM permissions to authenticate and pull from those repositories.
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const exampleContainerService = new aws.lightsail.ContainerService("example", {privateRegistryAccess: {
ecrImagePullerRole: {
isActive: true,
},
}});
const example = exampleContainerService.privateRegistryAccess.apply(privateRegistryAccess => aws.iam.getPolicyDocumentOutput({
statements: [{
effect: "Allow",
principals: [{
type: "AWS",
identifiers: [privateRegistryAccess.ecrImagePullerRole?.principalArn],
}],
actions: [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
],
}],
}));
const exampleRepositoryPolicy = new aws.ecr.RepositoryPolicy("example", {
repository: exampleAwsEcrRepository.name,
policy: example.apply(example => example.json),
});
import pulumi
import pulumi_aws as aws
example_container_service = aws.lightsail.ContainerService("example", private_registry_access={
"ecr_image_puller_role": {
"is_active": True,
},
})
example = example_container_service.private_registry_access.apply(lambda private_registry_access: aws.iam.get_policy_document_output(statements=[{
"effect": "Allow",
"principals": [{
"type": "AWS",
"identifiers": [private_registry_access.ecr_image_puller_role.principal_arn],
}],
"actions": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
],
}]))
example_repository_policy = aws.ecr.RepositoryPolicy("example",
repository=example_aws_ecr_repository["name"],
policy=example.json)
package main
import (
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/ecr"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lightsail"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
exampleContainerService, err := lightsail.NewContainerService(ctx, "example", &lightsail.ContainerServiceArgs{
PrivateRegistryAccess: &lightsail.ContainerServicePrivateRegistryAccessArgs{
EcrImagePullerRole: &lightsail.ContainerServicePrivateRegistryAccessEcrImagePullerRoleArgs{
IsActive: pulumi.Bool(true),
},
},
})
if err != nil {
return err
}
example := exampleContainerService.PrivateRegistryAccess.ApplyT(func(privateRegistryAccess lightsail.ContainerServicePrivateRegistryAccess) (iam.GetPolicyDocumentResult, error) {
return iam.GetPolicyDocumentResult(interface{}(iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement([]iam.GetPolicyDocumentStatement{
{
Effect: pulumi.StringRef(pulumi.String(pulumi.StringRef("Allow"))),
Principals: []iam.GetPolicyDocumentStatementPrincipal{
{
Type: "AWS",
Identifiers: interface{}{
privateRegistryAccess.EcrImagePullerRole.PrincipalArn,
},
},
},
Actions: []string{
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
},
},
}),
}, nil))), nil
}).(iam.GetPolicyDocumentResultOutput)
_, err = ecr.NewRepositoryPolicy(ctx, "example", &ecr.RepositoryPolicyArgs{
Repository: pulumi.Any(exampleAwsEcrRepository.Name),
Policy: pulumi.String(example.ApplyT(func(example iam.GetPolicyDocumentResult) (*string, error) {
return &example.Json, nil
}).(pulumi.StringPtrOutput)),
})
if err != nil {
return err
}
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;
return await Deployment.RunAsync(() =>
{
var exampleContainerService = new Aws.LightSail.ContainerService("example", new()
{
PrivateRegistryAccess = new Aws.LightSail.Inputs.ContainerServicePrivateRegistryAccessArgs
{
EcrImagePullerRole = new Aws.LightSail.Inputs.ContainerServicePrivateRegistryAccessEcrImagePullerRoleArgs
{
IsActive = true,
},
},
});
var example = Aws.Iam.GetPolicyDocument.Invoke(new()
{
Statements = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementArgs
{
Effect = "Allow",
Principals = new[]
{
new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalArgs
{
Type = "AWS",
Identifiers = new[]
{
exampleContainerService.PrivateRegistryAccess.EcrImagePullerRole?.PrincipalArn,
},
},
},
Actions = new[]
{
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
},
},
},
});
var exampleRepositoryPolicy = new Aws.Ecr.RepositoryPolicy("example", new()
{
Repository = exampleAwsEcrRepository.Name,
Policy = example.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lightsail.ContainerService;
import com.pulumi.aws.lightsail.ContainerServiceArgs;
import com.pulumi.aws.lightsail.inputs.ContainerServicePrivateRegistryAccessArgs;
import com.pulumi.aws.lightsail.inputs.ContainerServicePrivateRegistryAccessEcrImagePullerRoleArgs;
import com.pulumi.aws.iam.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.ecr.RepositoryPolicy;
import com.pulumi.aws.ecr.RepositoryPolicyArgs;
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 exampleContainerService = new ContainerService("exampleContainerService", ContainerServiceArgs.builder()
.privateRegistryAccess(ContainerServicePrivateRegistryAccessArgs.builder()
.ecrImagePullerRole(ContainerServicePrivateRegistryAccessEcrImagePullerRoleArgs.builder()
.isActive(true)
.build())
.build())
.build());
final var example = exampleContainerService.privateRegistryAccess().applyValue(_privateRegistryAccess -> IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
.statements(GetPolicyDocumentStatementArgs.builder()
.effect("Allow")
.principals(GetPolicyDocumentStatementPrincipalArgs.builder()
.type("AWS")
.identifiers(_privateRegistryAccess.ecrImagePullerRole().principalArn())
.build())
.actions(
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer")
.build())
.build()));
var exampleRepositoryPolicy = new RepositoryPolicy("exampleRepositoryPolicy", RepositoryPolicyArgs.builder()
.repository(exampleAwsEcrRepository.name())
.policy(example.applyValue(_example -> _example.json()))
.build());
}
}
resources:
exampleContainerService:
type: aws:lightsail:ContainerService
name: example
properties:
privateRegistryAccess:
ecrImagePullerRole:
isActive: true
exampleRepositoryPolicy:
type: aws:ecr:RepositoryPolicy
name: example
properties:
repository: ${exampleAwsEcrRepository.name}
policy: ${example.json}
variables:
example:
fn::invoke:
function: aws:iam:getPolicyDocument
arguments:
statements:
- effect: Allow
principals:
- type: AWS
identifiers:
- ${exampleContainerService.privateRegistryAccess.ecrImagePullerRole.principalArn}
actions:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
The privateRegistryAccess property enables ECR integration by creating an IAM role. Setting isActive to true generates a principalArn that you use in your ECR repository policy. The example shows how to grant the necessary pull permissions (BatchGetImage, GetDownloadUrlForLayer) to this principal.
Beyond these examples
These snippets focus on specific container service features: compute capacity sizing, custom domain mapping, and private registry authentication. They’re intentionally minimal rather than full container deployments.
The examples may reference pre-existing infrastructure such as SSL/TLS certificates for custom domains, ECR repositories for private images, and DNS configuration for domain routing. They focus on configuring the service platform rather than deploying actual containers.
To keep things focused, common container service patterns are omitted, including:
- Container deployment configuration (images, ports, environment variables)
- Service state management (isDisabled for pausing)
- Regional placement (region property)
- Load balancer and networking details
These omissions are intentional: the goal is to illustrate how each service feature is wired, not provide drop-in container deployment modules. See the Lightsail ContainerService resource reference for all available configuration options.
Let's deploy AWS Lightsail Container Services
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Custom Domains & SSL
publicDomainNames. Custom domains won’t work without a valid certificate.publicDomainNames. If you don’t configure custom domains, the service provides a default domain accessible via the url property.Configuration & Sizing
power parameter accepts: nano, micro, small, medium, large, or xlarge. Each power level determines the memory, vCPUs, and monthly price per node.scale parameter specifies the number of allocated compute nodes for your container service. It’s a required integer value.isDisabled to true. This parameter defaults to false when not specified.Naming & Immutability
name property is immutable. To change the name, you must destroy and recreate the resource.Private Registry & ECR
privateRegistryAccess with ecrImagePullerRole.isActive set to true. Then create an ECR repository policy granting ecr:BatchGetImage and ecr:GetDownloadUrlForLayer permissions to the generated principalArn.principalArn output property contains the ARN needed to create trust relationships between your AWS account and the container service. Use this in your ECR repository policy.Using a different cloud?
Explore containers guides for other cloud providers: