﻿# Configuration Options


> [!NOTE]
> The transport does not support `transport.ConnectionString(...)` to specify the connection string via code.

## CredentialSource

**Mandatory**

**Default**: AWS SDK credentials

By default the endpoint uses the SDK to retrieve AWS credentials. The AWS SDK permits a large number of transparent methods for configuring the credentials as outlined in the [.NET SDK guidelines](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html).

**Example**: To manually control the credentials retrieval, specify:

<!-- snippet: CredentialSource -->

```cs
var transport = new SqsTransport(
    new AmazonSQSClient(new InstanceProfileAWSCredentials()),
    new AmazonSimpleNotificationServiceClient());

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

for S3 specify

<!-- snippet: S3CredentialSource -->

```cs
var transport = new SqsTransport
{
    S3 = new S3Settings(bucketName, keyPrefix,
        new AmazonS3Client(new InstanceProfileAWSCredentials()))
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

## Region

**Mandatory**

**Default**: AWS SDK region

By default the endpoint uses the SDK to retrieve the default AWS region from the `AWS_REGION` environment variable.

This is the [Amazon Web Services Region](https://docs.aws.amazon.com/general/latest/gr/rande.html) in which to access the SQS service. The value must be a valid [AWS region code](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions).

**Example**: To manually control the region, specify

<!-- snippet: Region -->

```cs
var transport = new SqsTransport(new AmazonSQSClient(
    new AmazonSQSConfig
    {
        RegionEndpoint = RegionEndpoint.APSoutheast2
    }),
    new AmazonSimpleNotificationServiceClient());

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

for S3 specify

<!-- snippet: S3Region -->

```cs
var transport = new SqsTransport
{
    S3 = new S3Settings(bucketName, keyPrefix,
        new AmazonS3Client(new AmazonS3Config
        {
            RegionEndpoint = RegionEndpoint.APSoutheast2
        }))
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

## ProxyHost and ProxyPort

**Optional**

**Default**: Empty

This is the name of the host of the proxy server that the client must authenticate to.

<!-- snippet: Proxy -->

```cs
var transport = new SqsTransport(new AmazonSQSClient(
        new AmazonSQSConfig
        {
            ProxyCredentials = new NetworkCredential(userName, password),
            ProxyHost = "127.0.0.1",
            ProxyPort = 8888
        }),
    new AmazonSimpleNotificationServiceClient());

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

for S3 specify

<!-- snippet: S3Proxy -->

```cs
var transport = new SqsTransport
{
    S3 = new S3Settings(bucketName, keyPrefix,
        new AmazonS3Client(new AmazonS3Config
        {
            ProxyCredentials = new NetworkCredential(userName, password),
            ProxyHost = "127.0.0.1",
            ProxyPort = 8888
        }))
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

> [!NOTE]
> It is discouraged to specify username and password in code.

## SQS Client

**Optional**

**Default**: `new AmazonSQSClient()`

By default the transport uses a parameterless constructor to build the SQS client. This overrides the default SQS client with a custom one.

**Example**: To use a custom client, specify:

<!-- snippet: ClientFactory -->

```cs
var transport = new SqsTransport(
    new AmazonSQSClient(new AmazonSQSConfig()),
    new AmazonSimpleNotificationServiceClient());

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

> [!NOTE]
> If a custom SQS client is provided, it will not be disposed of when the endpoint is stopped.

## SNS Client

**Optional**

**Default**: `new AmazonSimpleNotificationServiceClient()`

By default the transport uses a parameterless constructor to build the SNS client. This overrides the default SNS client with a custom one.

**Example**: To use a custom client, specify:

<!-- snippet: SnsClientFactory -->

```cs
var transport = new SqsTransport(
    new AmazonSQSClient(new AmazonSQSConfig()),
    new AmazonSimpleNotificationServiceClient());

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

> [!NOTE]
> If a custom SNS client is provided, it will not be disposed of when the endpoint is stopped.


## Do not wrap message payload in a transport envelope

**Optional**

**Default**: `false`

By default the transport wraps outgoing messages in an envelope that contains also the message headers. The payload itself is encoded with Base64. This is done to ensure compatibility with endpoints running version 6.0 of the transport or below.

**Example**: To disable message wrapping and Base64-encoding of outgoing messages:

<!-- snippet: DoNotWrapOutgoingMessages -->

```cs
var transport = new SqsTransport
{
    DoNotWrapOutgoingMessages = true
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

> [!WARNING]
> This setting should only be enabled if all endpoints are running a version of the transport that contains this setting. ServiceControl should be on version 4.29.3 or above.

### Message Attributes

When the `DoNotWrapOutgoingMessages` setting is enabled, all NServiceBus headers are stored in the `NServiceBus.AmazonSQS.Headers` message attribute.
If the message is being sent to a non-NServiceBus endpoint, the consumer can use message attributes to handle a message in a particular way without having to process the message body first.

> [!NOTE]
> When sending messages from a non-NServiceBus endpoint to an NServiceBus endpoint, use UTF8 encoding and add the [`NServiceBus.AmazonSQS.Headers` message attribute](/transports/sqs/native-integration.md#message-type-detection) to ensure compatibility.


## Retention period

**Optional**

**Default**: 4 days

This is the maximum time that a message will be retained within SQS and S3. If a sent message is not received and successfully processed within the specified time, it will be lost. This value applies to both SQS and S3: messages in SQS will be deleted after this period, and large message bodies stored in S3 will be automatically deleted after this period.

The maximum value is 14 days.

**Example**: To set this to the maximum value, specify:

<!-- snippet: MaxTTL -->

```cs
var transport = new SqsTransport
{
    MaxTimeToLive = TimeSpan.FromDays(10)
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

> [!NOTE]
> [Large message payloads stored in S3](topology.md#s3) are never deleted by the receiving endpoint, regardless of whether they were successfully handled. The S3 aging policy controls payload deletion and respects the configured TTL. Since message payloads stored in S3 are important for audited and failed messages in ServiceControl, it is crucial that the [ServiceControl message retention period](/servicecontrol/how-purge-expired-data.md) aligns with the configured SQS and S3 TTLs.

## Queue name prefix

**Optional**

**Default**: None

This string value is prepended to the name of every SQS queue referenced by the endpoint. This is useful when deploying multiple instances of the same application in the same AWS region (e.g., development, QA, and production), and when the queue names must be distinguished from one another.

**Example**: For a development instance, specify:

<!-- snippet: QueueNamePrefix -->

```cs
var transport = new SqsTransport
{
    QueueNamePrefix = "DEV-"
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

For example, queue names for the endpoint called "SampleEndpoint" might be:

```
DEV-SampleEndpoint
DEV-SampleEndpoint-Retries
DEV-SampleEndpoint-Timeouts
DEV-SampleEndpoint-TimeoutsDispatcher
```

## Queue name generator

**Optional**

**Default**: `$"{queueNamePrefix}{queueName}` with unsupported characters like `.` are replaced with a hyphen `-`

Provides the ability to override the queue name generation with a custom function that allows creating queues in alignment with custom conventions.

<!-- snippet: QueueNameGenerator -->

```cs
var transport = new SqsTransport
{
    QueueNameGenerator = (name, prefix) =>
    {
        if (name.StartsWith(prefix))
        {
            return name;
        }

        return prefix + "-" + name;
    }
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

> [!NOTE]
> The provided function needs to be _idempotent_, i.e., apply the specified prefix only if it has not yet been applied.


## Offload large messages to S3

**Optional**

**Default**: Disabled. Any attempt to send a message larger than the SQS limit will fail.

This option configures the S3 bucket to be used to store messages larger than 1 MiB for commands and 256KiB for events. If this option is not specified, S3 will not be used at all and any attempt to send a message larger than the size limits will fail

If the specified bucket doesn't exist, it will be created when the endpoint starts.

**Example**: To use a bucket named `nsb-sqs-messages`, specify:

<!-- snippet: S3BucketForLargeMessages -->

```cs
var transport = new SqsTransport
{
    S3 = new S3Settings(
        bucketForLargeMessages: "nsb-sqs-messages",
        keyPrefix: "my/sample/path")
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

### Key prefix

**Mandatory**

This is the path in the specified S3 bucket for storing large messages.

### S3 Client

**Optional**

**Default**: `new AmazonS3Client()`

By default, the transport uses a parameterless constructor to build the S3 client. This overrides the default S3 client with a custom one.

**Example**: To use a custom client, specify:

<!-- snippet: S3ClientFactory -->

```cs
var transport = new SqsTransport
{
    S3 = new S3Settings(bucketName, keyPrefix,
        new AmazonS3Client(new AmazonS3Config()))
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->


> [!NOTE]
> If a custom S3 client is provided, it will not be disposed of when the endpoint is stopped.

### Encryption

**Optional**

**Default**: Disabled

Specifies how large messages stored in S3 are encrypted. The default option is no encryption. The alternative is to use a managed encryption key:

<!-- snippet: S3ServerSideEncryption -->

```cs
var transport = new SqsTransport
{
    S3 = new S3Settings(bucketName, keyPrefix)
    {
        Encryption = new S3EncryptionWithManagedKey(ServerSideEncryptionMethod.AES256, "keyId")
    }
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

or to provide a custom key:

<!-- snippet: S3ServerSideCustomerEncryption -->

```cs
var transport = new SqsTransport
{
    S3 = new S3Settings(bucketName, keyPrefix)
    {
        Encryption = new S3EncryptionWithCustomerProvidedKey(ServerSideEncryptionCustomerMethod.AES256, "key", "keyMD5")
    }
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

### Payload signing

<!-- snippet: DisablePayloadSigning -->

```cs
var transport = new SqsTransport
{
    S3 = new S3Settings(bucketName, keyPrefix)
    {
        DisablePayloadSigning = true
    }
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

Amazon S3 requires the payload to be signed when uploaded to the S3 bucket. The SQS transport allows disabling the payload signing by setting the `DisablePayloadSigning` to true to enable support for alternate storages, such as [Cloudflare R2](https://www.cloudflare.com/developer-platform/products/r2/).


## Message visibility

To prevent messages from being reprocessed while a handler is still executing, the transport automatically renews the message visibility timeout during processing. This is especially important for long-running handlers where the processing time may exceed the original visibility timeout.

The transport calculates when to renew the message visibility using the following logic:

```text
buffer = Min(remainingTime / 2, 10 seconds)
renewAfter = remainingTime - buffer
```

A message visibility timeout renewal is triggered after `renewAfter` has elapsed since the last renewal attempt. The renewal is attempted immediately if the remaining time is less than 400ms. This ensures the message visibility is extended consistently and early enough to avoid expiration, even under system load or scheduling delays.

When renewing, the transport extends the visibility timeout by:

```text
Max(Abs(remainingTimeInSeconds) + visibilityTimeoutInSeconds, visibilityTimeoutInSeconds)
```

This allows the visibility to be “pushed forward” by a consistent chunk while also compensating for delays. If a renewal is delayed and occurs after the visibility timeout expires, the transport tries to compensate, for example assuming the remaining time is `-2s` the new visibility timeout would be `abs(-2s) + 30s = 32s`. This is done because an expired visibility timeout doesn’t always mean the message can’t be completed—if no competing consumer picks it up, processing may still succeed.

**Example**:

- Configured visibility timeout: 30 seconds
- Max total extension duration: 5 minutes

```mermaid
sequenceDiagram
    participant Queue
    participant Handler

    Note over Queue,Handler: T+0s: Message received
    Queue->>Handler: Deliver message
    Note right of Handler: Initial visibility = 30s (expires at T+30s)

    Note over Handler: Processing...

    Note over Handler: Schedule renewal at T+20s
    Handler->>Queue: T+20s: Renew (remaining = 10s)
    Note right of Queue: Extend visibility to T+60s (extended by 10s + 30s = 40s)

    Note over Handler: Schedule next renewal at T+50s
    Handler->>Queue: T+50s: Renew (remaining = 10s)
    Note right of Queue: Extend visibility to T+90s (extended by 10s + 30s = 40s)

    Note over Handler: Schedule next renewal at T+80s
    Handler->>Queue: T+80s: Renew (remaining = 10s)
    Note right of Queue: Extend visibility to T+120s (extended by 10s + 30s = 40s)

    alt Delayed renewal (remaining < 0)
        Handler->>Queue: Overextend visibility by abs(remaining) + 30s
    end

    alt T+300s reached
        Note right of Handler: Stop renewing
    end

    alt Processing completes
        Handler->>Queue: Delete message
    else Handler still running
        Queue->>Handler: Message becomes visible again
    end
```

The message visibility timeout extension is a best effort operation and can fail due to several reasons including:

- Significant clock skew between the client and the SQS service time
- Network interruptions for extensive periods of time between the client and the SQS service
- Backpressure from the SQS service due to throttling

### MaxAutoMessageVisibilityRenewalDuration

**Optional**

**Default**: `TimeSpan.FromMinutes(5)`

This configures the allowed maximum message visibility timeout, after which the transport stops renewing the visibility. Amazon SQS supports a maximum value of 12 hours.

By setting it to `TimeSpan.Zero`, the message visibility renewal is disabled; thus, the default message visibility time applies when the message is received.

<!-- snippet: MaxAutoMessageVisibilityRenewalDuration -->

```cs
var transport = new SqsTransport
{
    MaxAutoMessageVisibilityRenewalDuration = TimeSpan.FromMinutes(15)
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

### MessageVisibilityTimeout

**Optional**

**Default**: `null`

By default, the transport acquires the message visibility timeout of the queue by reading the `MessageVisibilityTimeout` attribute. By setting an explicit value, the message visibility timeout on the queue is overruled by the timeout specified in this setting which will be used on every receive request.

<!-- snippet: MessageVisibilityTimeout -->

```cs
var transport = new SqsTransport
{
    MessageVisibilityTimeout = TimeSpan.FromMinutes(5)
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->






## Topic name prefix

**Optional**

**Default**: None

This string value is prepended to the name of every SNS topic subscribed by the endpoint. This is useful when deploying many instances of the same application in the same AWS region (e.g. a development instance, a QA instance, and a production instance), and the topic names must be distinguished from each other.

**Example**: For a development instance, specify:

<!-- snippet: TopicNamePrefix -->

```cs
var transport = new SqsTransport
{
    TopicNamePrefix = "DEV-"
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

For example, topic names for the topic called "MyNameSpace.MyEvent" might be:

```
DEV-MyNameSpace-MyEvent
```

## Topic name generator

**Optional**

**Default**: `$"{topicNamePrefix}{eventType.FullName}` with unsupported characters like `.` being replaced with a hyphen `-`

Provides the ability to override the topic name generation with a custom function that allows creating topics in alignment with custom conventions.

<!-- snippet: TopicNameGenerator -->

```cs
var transport = new SqsTransport
{
    TopicNameGenerator = (eventType, topicNamePrefix) => $"{topicNamePrefix}{eventType.Name}"
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

Be aware that ServiceControl doesn't allow customization of this convention when publishing ServiceControl events. ServiceControl events will be published using the default naming convention.

## Custom topics mappings

The [transport topology](topology.md#sqs-publishsubscribe) describes in depth how the topology is determined by subscribers. There are scenarios in which a custom mapping is needed.

The `MapEvent` transport configuration API can be used to customize the way subscribers determine the topic to subscribe to. If the subscribers have knowledge of both the published event type and the subscribed one, the following API can be used:

<!-- snippet: CustomTopicsMappingsTypeToType -->

```cs
var transport = new SqsTransport();

transport.MapEvent<SubscribedEvent, PublishedEvent>();

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

> [!NOTE]
> The types are only used to determine the topic name; subscribers can define dummy empty types to use the strongly typed API shown above.

If the published type is not known at compilation time, the following API can be used:

<!-- snippet: CustomTopicsMappingsTypeToTopic -->

```cs
var transport = new SqsTransport();

transport.MapEvent<SubscribedEvent>("topic-used-by-the-publisher");

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

## Policy

> [!NOTE]
> When an endpoint is starting the [auto-subscribe mechanism](/nservicebus/messaging/publish-subscribe/controlling-what-is-subscribed.md#automatic-subscriptions) ensures the necessary SNS topics for the events are created and all subscriptions are set up to receive the events published to the topics. AWS [IAM policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html) offer very fine-grained control of access to services and resources.

NServiceBus automatically subscribes to all event types an endpoint has handlers for. For example, an endpoint may have two handlers:

```c#
public class OrderAcceptedHandler : IHandleMessages<OrderAccepted> { ... }
public class OrderPaidHandler : IHandleMessages<OrderPaid> { ... }
```

The transport creates a policy statement for the event types it subscribes to:

```json
{
  ...
  "Statement": [
    {
      ...
      "Action": "sqs:SendMessage",
      "Resource": "arn:aws:sqs:some-region:some-account:endpoint",
      "Condition": {
        "ArnLike": {
          "aws:SourceArn": [
            "arn:aws:sns:some-region:some-account:Sales-OrderAccepted",
            "arn:aws:sns:some-region:some-account:Sales-OrderPaid"
          ]
        }
      }
    }
  ]
}
```

The policy statement is updated when an endpoint explicitly subscribes to an event type using [`session.Subscribe<CustomEvent>()`](/nservicebus/messaging/publish-subscribe/controlling-what-is-subscribed.md). Unsubscribing does not modify the policy.

### Wildcards

#### Account condition

Allow all messages from any topic in the account. The account name is extracted from the subscribed topic ARN.

<!-- snippet: wildcard-account-condition -->

```cs
var transport = new SqsTransport();

transport.Policies.AccountCondition = true;

config.UseTransport(transport);
```

<!-- endsnippet -->

#### Prefix condition

Allow all messages from any topic with the specified [topic name prefix](#topic-name-prefix).

<!-- snippet: wildcard-prefix-condition -->

```cs
var transport = new SqsTransport();

transport.Policies.TopicNamePrefixCondition = true;

config.UseTransport(transport);
```

<!-- endsnippet -->

#### Namespace condition

Allow all messages in specific namespaces.

<!-- snippet: wildcard-namespace-condition -->

```cs
var transport = new SqsTransport();

transport.Policies.TopicNamespaceConditions
    .Add("Sales.");
transport.Policies.TopicNamespaceConditions
    .Add("Shipping.HighValueOrders.");

config.UseTransport(transport);
```

<!-- endsnippet -->

### Disabling runtime policy modification

If the policy is modified during deployment it may be better to disable runtime policy modification.

<!-- snippet: assume-permissions -->

```cs
var transport = new SqsTransport();

transport.Policies.SetupTopicPoliciesWhenSubscribing = false;

config.UseTransport(transport);
```

<!-- endsnippet -->


## Message-driven publish/subscribe compatibility mode

To gradually migrate an existing system from message-driven publish/subscribe to native publish/subscribe using SNS, it's possible to enable message-driven publish/subscribe compatibility mode.

Message-driven publish/subscribe compatibility mode must be enabled on publisher endpoints. When enabled, publishers will still consume subscription messages sent by endpoints via message-driven publish/subscribe and published events will be sent to both legacy subscribers and SNS. Publishers deduplicate published events.

> [!WARNING]
> Starting from version 9.1 of the transport, publish/subscribe compatibility mode is deprecated.
>
> See [the upgrade guide](/transports/upgrades/amazonsqs-9to10.md) for more details.

To enable message-driven publish/subscribe compatibility mode, configure the endpoint as follows:

<!-- snippet: EnableMessageDrivenPubSubCompatibilityMode -->

```cs
var routing = endpointConfiguration.UseTransport(new SqsTransport());

routing.EnableMessageDrivenPubSubCompatibilityMode();
```

<!-- endsnippet -->

### Subscription cache configuration

The default value for SNS topic subscription cache invalidation (5 seconds) can be changed using:

<!-- snippet: SubscriptionsCacheTTL -->

```cs
var migrationSettings = routing.EnableMessageDrivenPubSubCompatibilityMode();
migrationSettings.SubscriptionsCacheTTL(TimeSpan.FromSeconds(30));
```

<!-- endsnippet -->

### Topic cache configuration

The default value for SNS topic cache invalidation (5 seconds) can be changed using:

<!-- snippet: TopicCacheTTL -->

```cs
var migrationSettings = routing.EnableMessageDrivenPubSubCompatibilityMode();
migrationSettings.TopicCacheTTL(TimeSpan.FromSeconds(30));
```

<!-- endsnippet -->

### Message visibility timeout

The default value for the message visibility timeout setting (30 seconds) can be changed using:

<!-- snippet: MessageVisibilityTimeout -->

```cs
var transport = new SqsTransport
{
    MessageVisibilityTimeout = TimeSpan.FromMinutes(5)
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->


## Reserve bytes when calculating message size

<!-- snippet: ReserveBytesInMessageSizeCalculation -->

```cs
var transport = new SqsTransport
{
    ReserveBytesInMessageSizeCalculation = 5*1024 // 5KB for additional metadata
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

Amazon [SQS](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/quotas-messages.html) and [SNS](https://docs.aws.amazon.com/general/latest/gr/sns.html) allows for a maximum message size of 256KiB.

In specific scenarios, third-party tools, such as monitoring tools, may add additional information to outgoing messages, causing the message size to overflow and messages to be rejected by the infrastructure when sent. The `ReserveBytesInMessageSizeCalculation` can specify a number of bytes between 0 and 25 * 1024 that will be added to the calculated payload size. It is helpful to account for any overhead of message attributes added outside the scope of NServiceBus to address the SQS service message size limitation by uploading the message payload to S3.

