Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ builder.AddLocalStack(configureContainer: container =>
// Eagerly load specific services for faster startup
container.EagerLoadedServices = [AwsService.Sqs, AwsService.DynamoDB, AwsService.S3];

// Recommended: Clean up container when application stops
container.Lifetime = ContainerLifetime.Session;
// Optional: Use Persistent lifetime for container reuse between runs
// (Default is Session - container cleaned up when application stops)
container.Lifetime = ContainerLifetime.Persistent;

// Optional: Enable verbose logging for troubleshooting
container.DebugLevel = 1;
Expand All @@ -112,10 +113,13 @@ builder.AddLocalStack(configureContainer: container =>
**Available Options:**

- **`EagerLoadedServices`** - Pre-load specific AWS services at startup (reduces cold start latency)
- **`Lifetime`** - Container lifecycle: `Persistent` (survives restarts) or `Session` (cleaned up on stop)
- **`Lifetime`** - Container lifecycle: `Session` (default - cleaned up on stop) or `Persistent` (survives restarts)
- **`DebugLevel`** - LocalStack debug verbosity (0 = default, 1 = verbose)
- **`LogLevel`** - Log level control (Error, Warn, Info, Debug, Trace, etc.)
- **`Port`** - Static port mapping for LocalStack container. If set, LocalStack will be mapped to this static port on the host. If not set, a dynamic port will be used unless the container lifetime is persistent, in which case the default LocalStack port (4566) is used. Useful for avoiding port conflicts or for predictable endpoint URLs
- **`Port`** - Static port mapping for LocalStack container. If not set, Session lifetime uses dynamic ports (avoids conflicts) and Persistent lifetime uses port 4566 (default LocalStack port). Set explicitly for predictable endpoint URLs
- **`ContainerRegistry`** - Custom container registry (default: `docker.io`). Use when pulling from private registries
- **`ContainerImage`** - Custom image name (default: `localstack/localstack`). Use when image is mirrored with different path
- **`ContainerImageTag`** - Custom image tag/version (default: package version). Use to pin to specific LocalStack version
- **`AdditionalEnvironmentVariables`** - Custom environment variables for advanced scenarios

For detailed configuration guide and best practices, see [Configuration Documentation](docs/CONFIGURATION.md).
Expand Down
235 changes: 220 additions & 15 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ This guide covers configuration options for customizing LocalStack container beh
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `EagerLoadedServices` | `IReadOnlyCollection<AwsService>` | `[]` (empty) | AWS services to pre-load at container startup |
| `Lifetime` | `ContainerLifetime` | `Persistent` | Container lifecycle behavior |
| `Lifetime` | `ContainerLifetime` | `Session` | Container lifecycle behavior |
| `Port` | `int?` | `null` | Static port mapping for LocalStack container |
| `ContainerRegistry` | `string?` | `null` (`docker.io`) | Custom container registry |
| `ContainerImage` | `string?` | `null` (`localstack/localstack`) | Custom container image name |
| `ContainerImageTag` | `string?` | `null` (package version) | Custom container image tag/version |
| `EnableDockerSocket` | `bool` | `false` | Mount Docker socket for Lambda support |
| `DebugLevel` | `int` | `0` | LocalStack DEBUG flag (0 or 1) |
| `LogLevel` | `LocalStackLogLevel` | `Error` | LocalStack LS_LOG level |
Expand Down Expand Up @@ -75,7 +79,23 @@ For a complete list of supported services, check the [LocalStack health endpoint

Controls when the LocalStack container is created and destroyed.

### Persistent (Default)
### Session (Default - Recommended)

Container is created when the application starts and destroyed when it stops. This is the default lifetime and aligns with Aspire's conventions.

```csharp
// Default - no need to set explicitly
container.Lifetime = ContainerLifetime.Session;
```

**When to use:**

- CI/CD pipelines (clean slate for each run)
- Integration tests (isolated test runs)
- When you want guaranteed clean state
- Most development scenarios

### Persistent

Container survives application restarts. Data may persist between debugging sessions depending on your configuration.

Expand All @@ -85,24 +105,175 @@ container.Lifetime = ContainerLifetime.Persistent;

**When to use:**

- Local development (container reuse between runs)
- Local development when you want container reuse between runs
- When combined with [LocalStack persistence](https://docs.localstack.cloud/aws/capabilities/state-management/persistence/)
- When working with large infrastructure that's slow to provision

## Port Configuration

Controls how LocalStack's port is mapped from the container to the host machine.

### Session
### Default Behavior

Container is created when the application starts and destroyed when it stops.
By default, port mapping depends on container lifetime:

```csharp
// Session lifetime (Default) - uses dynamic port assignment
container.Lifetime = ContainerLifetime.Session;
// Port will be: random available port

// Persistent lifetime - uses default LocalStack port (4566)
container.Lifetime = ContainerLifetime.Persistent;
// Port will be: 4566
```

**When to use:**
### Static Port Mapping

- CI/CD pipelines (clean slate for each run)
- Integration tests (isolated test runs)
- When you want guaranteed clean state
You can explicitly specify a port to ensure predictable endpoint URLs:

```csharp
container.Port = 4566; // Always use port 4566
```

### When to Use Static Ports

**Use static ports when:**

- You need predictable endpoint URLs across runs
- External tools need to connect to a known port
- You're integrating with legacy systems expecting specific ports
- Debugging network issues and need consistency

**Use dynamic ports when:**

- Running multiple instances simultaneously (tests, parallel development)
- Avoiding port conflicts with other services
- In CI/CD environments with parallel builds

### Port Conflict Resolution

If you encounter port conflicts:

```csharp
// Option 1: Use a different static port
container.Port = 4567;

// Option 2: Switch to Session lifetime for dynamic port assignment
container.Lifetime = ContainerLifetime.Session;
// Dynamic ports are used automatically when Lifetime = Session and Port is not set
```

## Custom Container Registry

Configure LocalStack to pull from private registries or container mirrors.

### Why Use Custom Registries?

Organizations often need to pull images from:

- **Private registries** (Artifactory, Harbor) for compliance/security
- **Container mirrors** to avoid Docker Hub rate limits
- **Internal registries** (Azure Container Registry, AWS ECR) for air-gapped environments
- **Custom builds** with organization-specific configurations

### Configuration Properties

Three properties work together to specify the complete image location:

```csharp
builder.AddLocalStack(configureContainer: container =>
{
container.ContainerRegistry = "artifactory.company.com"; // Where to pull from
container.ContainerImage = "docker-mirrors/localstack/localstack"; // Image path
container.ContainerImageTag = "4.10.0"; // Specific version
});
```

**Defaults:**

- `ContainerRegistry`: `docker.io` (Docker Hub)
- `ContainerImage`: `localstack/localstack`
- `ContainerImageTag`: Matches package version (e.g., `4.10.0`)

### Common Scenarios

#### Artifactory

```csharp
container.ContainerRegistry = "artifactory.company.com";
container.ContainerImage = "docker-local/localstack/localstack";
container.ContainerImageTag = "4.10.0";
// Pulls: artifactory.company.com/docker-local/localstack/localstack:4.10.0
```

**Recommendation:** Use `Session` for CI/CD and integration tests, `Persistent` for local development.
#### Azure Container Registry (ACR)

```csharp
container.ContainerRegistry = "mycompany.azurecr.io";
container.ContainerImage = "localstack/localstack";
container.ContainerImageTag = "4.10.0";
// Pulls: mycompany.azurecr.io/localstack/localstack:4.10.0
```

#### AWS Elastic Container Registry (ECR)

```csharp
container.ContainerRegistry = "123456789012.dkr.ecr.us-west-2.amazonaws.com";
container.ContainerImage = "localstack/localstack";
container.ContainerImageTag = "4.10.0";
// Pulls: 123456789012.dkr.ecr.us-west-2.amazonaws.com/localstack/localstack:4.10.0
```

#### GitHub Container Registry (GHCR)

```csharp
container.ContainerRegistry = "ghcr.io";
container.ContainerImage = "myorg/localstack";
container.ContainerImageTag = "custom-build-123";
// Pulls: ghcr.io/myorg/localstack:custom-build-123
```

#### Pin to Specific Version

```csharp
// Only override the tag to use a different LocalStack version
container.ContainerImageTag = "3.8.1";
// Pulls: docker.io/localstack/localstack:3.8.1
```

### Authentication

Container registry authentication is handled by Docker/Podman on the host machine. Ensure you're logged in before running:

```bash
# Docker Hub
docker login

# Private registry
docker login artifactory.company.com

# Azure Container Registry
az acr login --name mycompany

# AWS ECR
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-west-2.amazonaws.com
```

### Backward Compatibility

All three properties are optional and default to the public Docker Hub image:

```csharp
// These are equivalent:
builder.AddLocalStack();

builder.AddLocalStack(configureContainer: container =>
{
container.ContainerRegistry = "docker.io";
container.ContainerImage = "localstack/localstack";
container.ContainerImageTag = "4.10.0"; // Package version
});
```

## Logging and Debugging

Expand Down Expand Up @@ -185,39 +356,71 @@ container.AdditionalEnvironmentVariables["PERSISTENCE"] = "1";

## Configuration Patterns

### Development
### Development (Fast Iteration)

```csharp
builder.AddLocalStack(configureContainer: container =>
{
container.Lifetime = ContainerLifetime.Persistent;
// Default Session lifetime is fine for most development
container.LogLevel = LocalStackLogLevel.Warn;
// Use lazy loading for faster startup
});
```

### Development (Container Reuse)

```csharp
builder.AddLocalStack(configureContainer: container =>
{
// Use Persistent to reuse container between runs
container.Lifetime = ContainerLifetime.Persistent;
container.LogLevel = LocalStackLogLevel.Warn;
});
```

### CI/CD

```csharp
builder.AddLocalStack(configureContainer: container =>
{
container.Lifetime = ContainerLifetime.Session;
// Default Session lifetime is perfect for CI/CD
container.LogLevel = LocalStackLogLevel.Error;

// Eagerly load all services used in tests
container.EagerLoadedServices = [AwsService.Sqs, AwsService.DynamoDB, AwsService.S3];
});
```

### Enterprise with Private Registry

```csharp
builder.AddLocalStack(configureContainer: container =>
{
// Pull from private Artifactory
container.ContainerRegistry = "artifactory.company.com";
container.ContainerImage = "docker-local/localstack/localstack";
container.ContainerImageTag = "4.10.0";

container.Lifetime = ContainerLifetime.Persistent;
container.LogLevel = LocalStackLogLevel.Warn;

// Use static port for consistency with other tools
container.Port = 4566;
});
```

### Debugging

```csharp
builder.AddLocalStack(configureContainer: container =>
{
container.Lifetime = ContainerLifetime.Session;
// Default Session lifetime - clean state for each debug session
container.LogLevel = LocalStackLogLevel.Debug;
container.DebugLevel = 1;

// Use static port for easier debugging
container.Port = 4566;

// Eagerly load to see startup issues
container.EagerLoadedServices = [AwsService.Sqs];
});
Expand All @@ -228,9 +431,11 @@ builder.AddLocalStack(configureContainer: container =>
```csharp
builder.AddLocalStack(configureContainer: container =>
{
container.Lifetime = ContainerLifetime.Session;
// Default Session lifetime - perfect for isolated test runs
container.LogLevel = LocalStackLogLevel.Error;

// Dynamic ports by default - allows parallel test runs without conflicts

// Eagerly load services to avoid cold-start variance
container.EagerLoadedServices = [AwsService.Sqs, AwsService.DynamoDB];
});
Expand Down
Loading