Deploy AWS Lightsail Container Services

The aws:lightsail/containerService:ContainerService resource, part of the Pulumi AWS provider, provisions the Lightsail container service platform: compute nodes, networking, and access configuration. This guide focuses on three capabilities: compute capacity configuration, custom domain mapping, and ECR private registry access.

Container services require validated SSL/TLS certificates for custom domains and may reference ECR repositories for private images. The examples are intentionally small. Combine them with your own container deployments, certificates, and image repositories.

Create a container service with compute capacity

Most deployments start by defining compute capacity and naming the service for identification.

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 sets the node size (nano through xlarge), determining memory, vCPUs, and monthly cost per node. The scale property sets how many nodes run. Together, these define your total compute capacity. The isDisabled property lets you pause the service without deleting it.

Map custom domains to the container service

Applications serving public traffic often need custom domains instead of the default Lightsail URL.

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 custom domains to your service. Each certificate can cover multiple domains. You must create and validate an SSL/TLS certificate in Lightsail before using it here; the certificateName references that certificate. DNS records must point to the container service for traffic to reach it.

Pull images from Amazon ECR private repositories

Teams storing images in private ECR repositories need to grant the service permission to pull them.

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. Setting isActive to true creates an IAM role that can pull images. The example shows how to use the service’s principalArn to create an ECR repository policy that grants pull permissions. This lets your container service authenticate to ECR and pull private images during deployment.

Beyond these examples

These snippets focus on specific container service features: compute capacity sizing, custom domain mapping with SSL/TLS, and ECR private registry integration. 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 with container images, and DNS configuration pointing to the service. They focus on configuring the service platform rather than deploying containers.

To keep things focused, common container service patterns are omitted, including:

  • Container deployment configuration (images, ports, environment variables)
  • Health checks and monitoring
  • Scaling policies and auto-scaling
  • Network configuration and VPC peering

These omissions are intentional: the goal is to illustrate how each service feature is wired, not provide drop-in container platforms. 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 FREE

Frequently Asked Questions

Configuration & Setup
What are the available power specifications and what do they control?
Power options are nano, micro, small, medium, large, and xlarge. Each power level determines the memory, number of vCPUs, and monthly price per node.
What's the minimum configuration needed to create a container service?
You need three required properties: name (1-63 characters, unique per region), power (e.g., nano), and scale (number of compute nodes).
Can I rename my container service after creation?
No, the name property is immutable. Changing it requires destroying and recreating the service, so plan your naming carefully.
Custom Domains & SSL
Why can't I use custom domains with my container service?
You must create and validate an SSL/TLS certificate before configuring publicDomainNames. Custom domains won’t work without a validated certificate.
How many custom domains can I configure?
You can specify up to four public domain names using publicDomainNames. Each domain requires certificate configuration.
What happens if I don't specify custom domains?
The container service uses a default domain automatically. You can access this via the url output property.
Private Registry Access
How do I enable my container service to pull images from Amazon ECR?
Configure privateRegistryAccess with ecrImagePullerRole.isActive set to true, then create an ECR repository policy granting access to the principalArn output property.
Resource Management
How do I temporarily stop my container service without deleting it?
Set isDisabled to true. This disables the service while preserving its configuration. The default is false.
Can I create container services in all AWS Regions?
No, Lightsail container services are only available in specific AWS Regions. Check the AWS documentation for current regional availability.

Using a different cloud?

Explore containers guides for other cloud providers: