diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2161e1144..5150a1671 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -90,6 +90,7 @@
+
diff --git a/Testcontainers.sln b/Testcontainers.sln
index 30abbbec5..9a148f2ad 100644
--- a/Testcontainers.sln
+++ b/Testcontainers.sln
@@ -128,6 +128,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Sftp", "src\Testcontainers.Sftp\Testcontainers.Sftp.csproj", "{7D5C6816-0DD2-4E13-A585-033B5D3C80D5}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Temporal", "src\Testcontainers.Temporal\Testcontainers.Temporal.csproj", "{24431BF1-7BEB-4C53-BAE8-B9D9F622A240}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Toxiproxy", "src\Testcontainers.Toxiproxy\Testcontainers.Toxiproxy.csproj", "{65A47BA4-4DC8-4206-9B00-CBC87FC944FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Typesense", "src\Testcontainers.Typesense\Testcontainers.Typesense.csproj", "{E044A94A-3081-4EE4-8DC6-81601F96DA14}"
@@ -266,6 +268,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus.T
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Sftp.Tests", "tests\Testcontainers.Sftp.Tests\Testcontainers.Sftp.Tests.csproj", "{B73C3CC0-9F16-4B34-92BE-6EC0853912C5}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Temporal.Tests", "tests\Testcontainers.Temporal.Tests\Testcontainers.Temporal.Tests.csproj", "{28B5DEDF-C19B-4A7E-B276-FC4C83DBB7EF}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Tests", "tests\Testcontainers.Tests\Testcontainers.Tests.csproj", "{27CDB869-A150-4593-958F-6F26E5391E7C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Toxiproxy.Tests", "tests\Testcontainers.Toxiproxy.Tests\Testcontainers.Toxiproxy.Tests.csproj", "{10726AAA-E93F-4B40-A05E-28308423DABE}"
@@ -502,6 +506,10 @@ Global
{7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D5C6816-0DD2-4E13-A585-033B5D3C80D5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {24431BF1-7BEB-4C53-BAE8-B9D9F622A240}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {24431BF1-7BEB-4C53-BAE8-B9D9F622A240}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {24431BF1-7BEB-4C53-BAE8-B9D9F622A240}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {24431BF1-7BEB-4C53-BAE8-B9D9F622A240}.Release|Any CPU.Build.0 = Release|Any CPU
{65A47BA4-4DC8-4206-9B00-CBC87FC944FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{65A47BA4-4DC8-4206-9B00-CBC87FC944FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{65A47BA4-4DC8-4206-9B00-CBC87FC944FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -778,6 +786,10 @@ Global
{B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B73C3CC0-9F16-4B34-92BE-6EC0853912C5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {28B5DEDF-C19B-4A7E-B276-FC4C83DBB7EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {28B5DEDF-C19B-4A7E-B276-FC4C83DBB7EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {28B5DEDF-C19B-4A7E-B276-FC4C83DBB7EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {28B5DEDF-C19B-4A7E-B276-FC4C83DBB7EF}.Release|Any CPU.Build.0 = Release|Any CPU
{27CDB869-A150-4593-958F-6F26E5391E7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{27CDB869-A150-4593-958F-6F26E5391E7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{27CDB869-A150-4593-958F-6F26E5391E7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -866,6 +878,7 @@ Global
{45D6F69C-4D87-4130-AA90-0DB2F7460DAE} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{2E39E532-B81E-4B48-A004-FAE18EDF9E79} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{7D5C6816-0DD2-4E13-A585-033B5D3C80D5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
+ {24431BF1-7BEB-4C53-BAE8-B9D9F622A240} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{65A47BA4-4DC8-4206-9B00-CBC87FC944FC} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{E044A94A-3081-4EE4-8DC6-81601F96DA14} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{68F8600D-24E9-4E03-9E25-5F6EB338EAC1} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
@@ -935,6 +948,7 @@ Global
{9E8E6AA5-65D1-498F-BEAB-BA34723A0050} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{232DD918-46ED-4BA8-B383-1A9146D83064} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{B73C3CC0-9F16-4B34-92BE-6EC0853912C5} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
+ {28B5DEDF-C19B-4A7E-B276-FC4C83DBB7EF} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{27CDB869-A150-4593-958F-6F26E5391E7C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{10726AAA-E93F-4B40-A05E-28308423DABE} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{73CC8E45-5608-1398-4029-0802428B5565} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
diff --git a/Testcontainers.slnx b/Testcontainers.slnx
index f7304fdfb..e6a84283a 100644
--- a/Testcontainers.slnx
+++ b/Testcontainers.slnx
@@ -64,6 +64,7 @@
+
@@ -135,6 +136,7 @@
+
diff --git a/src/Testcontainers.Temporal/.editorconfig b/src/Testcontainers.Temporal/.editorconfig
new file mode 100644
index 000000000..6f066619d
--- /dev/null
+++ b/src/Testcontainers.Temporal/.editorconfig
@@ -0,0 +1 @@
+root = true
\ No newline at end of file
diff --git a/src/Testcontainers.Temporal/TemporalBuilder.cs b/src/Testcontainers.Temporal/TemporalBuilder.cs
new file mode 100644
index 000000000..fd8fc41ae
--- /dev/null
+++ b/src/Testcontainers.Temporal/TemporalBuilder.cs
@@ -0,0 +1,94 @@
+namespace Testcontainers.Temporal;
+
+///
+[PublicAPI]
+public sealed class TemporalBuilder : ContainerBuilder
+{
+ public const ushort TemporalGrpcPort = 7233;
+
+ public const ushort TemporalHttpPort = 8233;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The full Docker image name, including the image repository and tag
+ /// (e.g., temporalio/temporal:1.5.1).
+ ///
+ ///
+ /// Docker image tags available at .
+ ///
+ public TemporalBuilder(string image)
+ : this(new DockerImage(image))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// An instance that specifies the Docker image to be used
+ /// for the container builder configuration.
+ ///
+ ///
+ /// Docker image tags available at .
+ ///
+ public TemporalBuilder(IImage image)
+ : this(new TemporalConfiguration())
+ {
+ DockerResourceConfiguration = Init().WithImage(image).DockerResourceConfiguration;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ private TemporalBuilder(TemporalConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ DockerResourceConfiguration = resourceConfiguration;
+ }
+
+ ///
+ protected override TemporalConfiguration DockerResourceConfiguration { get; }
+
+ ///
+ public override TemporalContainer Build()
+ {
+ Validate();
+ return new TemporalContainer(DockerResourceConfiguration);
+ }
+
+ ///
+ protected override TemporalBuilder Init()
+ {
+ return base.Init()
+ .WithPortBinding(TemporalGrpcPort, true)
+ .WithPortBinding(TemporalHttpPort, true)
+ .WithCommand("server", "start-dev", "--ip", "0.0.0.0")
+ .WithConnectionStringProvider(new TemporalConnectionStringProvider())
+ .WithWaitStrategy(Wait.ForUnixContainer()
+ .UntilExternalTcpPortIsAvailable(TemporalGrpcPort)
+ .UntilExternalTcpPortIsAvailable(TemporalHttpPort)
+ .UntilHttpRequestIsSucceeded(request =>
+ request.ForPath("/api/v1/system-info").ForPort(TemporalHttpPort)));
+ }
+
+ ///
+ protected override TemporalBuilder Clone(IResourceConfiguration resourceConfiguration)
+ {
+ return Merge(DockerResourceConfiguration, new TemporalConfiguration(resourceConfiguration));
+ }
+
+ ///
+ protected override TemporalBuilder Clone(IContainerConfiguration resourceConfiguration)
+ {
+ return Merge(DockerResourceConfiguration, new TemporalConfiguration(resourceConfiguration));
+ }
+
+ ///
+ protected override TemporalBuilder Merge(TemporalConfiguration oldValue, TemporalConfiguration newValue)
+ {
+ return new TemporalBuilder(new TemporalConfiguration(oldValue, newValue));
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Temporal/TemporalConfiguration.cs b/src/Testcontainers.Temporal/TemporalConfiguration.cs
new file mode 100644
index 000000000..b9b9051c0
--- /dev/null
+++ b/src/Testcontainers.Temporal/TemporalConfiguration.cs
@@ -0,0 +1,53 @@
+namespace Testcontainers.Temporal;
+
+///
+[PublicAPI]
+public sealed class TemporalConfiguration : ContainerConfiguration
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TemporalConfiguration()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public TemporalConfiguration(IResourceConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ // Passes the configuration upwards to the base implementations to create an updated immutable copy.
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public TemporalConfiguration(IContainerConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ // Passes the configuration upwards to the base implementations to create an updated immutable copy.
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public TemporalConfiguration(TemporalConfiguration resourceConfiguration)
+ : this(new TemporalConfiguration(), resourceConfiguration)
+ {
+ // Passes the configuration upwards to the base implementations to create an updated immutable copy.
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The old Docker resource configuration.
+ /// The new Docker resource configuration.
+ public TemporalConfiguration(TemporalConfiguration oldValue, TemporalConfiguration newValue)
+ : base(oldValue, newValue)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Temporal/TemporalConnectionStringProvider.cs b/src/Testcontainers.Temporal/TemporalConnectionStringProvider.cs
new file mode 100644
index 000000000..c48bd57e9
--- /dev/null
+++ b/src/Testcontainers.Temporal/TemporalConnectionStringProvider.cs
@@ -0,0 +1,13 @@
+namespace Testcontainers.Temporal;
+
+///
+/// Provides the Temporal connection string.
+///
+internal sealed class TemporalConnectionStringProvider : ContainerConnectionStringProvider
+{
+ ///
+ protected override string GetHostConnectionString()
+ {
+ return Container.GetGrpcAddress();
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Temporal/TemporalContainer.cs b/src/Testcontainers.Temporal/TemporalContainer.cs
new file mode 100644
index 000000000..64336dc44
--- /dev/null
+++ b/src/Testcontainers.Temporal/TemporalContainer.cs
@@ -0,0 +1,45 @@
+namespace Testcontainers.Temporal;
+
+///
+[PublicAPI]
+public sealed class TemporalContainer : DockerContainer
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The container configuration.
+ public TemporalContainer(TemporalConfiguration configuration)
+ : base(configuration)
+ {
+ }
+
+ ///
+ /// Gets the Temporal gRPC address.
+ ///
+ ///
+ /// The Temporal SDK (client library) expects host:port without a scheme.
+ ///
+ ///
+ ///
+ /// var clientOptions = new TemporalClientConnectOptions();
+ /// clientOptions.TargetHost = temporalContainer.GetGrpcAddress();
+ ///
+ /// var connectedClient = await TemporalClient.ConnectAsync(clientOptions);
+ ///
+ ///
+ ///
+ /// The Temporal gRPC address in host:port format.
+ public string GetGrpcAddress()
+ {
+ return Hostname + ":" + GetMappedPublicPort(TemporalBuilder.TemporalGrpcPort);
+ }
+
+ ///
+ /// Gets the Temporal Web UI address.
+ ///
+ /// The Temporal Web UI base address.
+ public string GetWebUiAddress()
+ {
+ return new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(TemporalBuilder.TemporalHttpPort)).ToString();
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Temporal/Testcontainers.Temporal.csproj b/src/Testcontainers.Temporal/Testcontainers.Temporal.csproj
new file mode 100644
index 000000000..6f204b739
--- /dev/null
+++ b/src/Testcontainers.Temporal/Testcontainers.Temporal.csproj
@@ -0,0 +1,12 @@
+
+
+ net8.0;net9.0;net10.0;netstandard2.0;netstandard2.1
+ latest
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Testcontainers.Temporal/Usings.cs b/src/Testcontainers.Temporal/Usings.cs
new file mode 100644
index 000000000..26427f77f
--- /dev/null
+++ b/src/Testcontainers.Temporal/Usings.cs
@@ -0,0 +1,7 @@
+global using System;
+global using Docker.DotNet.Models;
+global using DotNet.Testcontainers.Builders;
+global using DotNet.Testcontainers.Configurations;
+global using DotNet.Testcontainers.Containers;
+global using DotNet.Testcontainers.Images;
+global using JetBrains.Annotations;
\ No newline at end of file
diff --git a/tests/Testcontainers.Temporal.Tests/.editorconfig b/tests/Testcontainers.Temporal.Tests/.editorconfig
new file mode 100644
index 000000000..6f066619d
--- /dev/null
+++ b/tests/Testcontainers.Temporal.Tests/.editorconfig
@@ -0,0 +1 @@
+root = true
\ No newline at end of file
diff --git a/tests/Testcontainers.Temporal.Tests/.runs-on b/tests/Testcontainers.Temporal.Tests/.runs-on
new file mode 100644
index 000000000..d0395e498
--- /dev/null
+++ b/tests/Testcontainers.Temporal.Tests/.runs-on
@@ -0,0 +1 @@
+ubuntu-24.04
\ No newline at end of file
diff --git a/tests/Testcontainers.Temporal.Tests/Dockerfile b/tests/Testcontainers.Temporal.Tests/Dockerfile
new file mode 100644
index 000000000..8a47f2a34
--- /dev/null
+++ b/tests/Testcontainers.Temporal.Tests/Dockerfile
@@ -0,0 +1 @@
+FROM temporalio/temporal:1.5.1
\ No newline at end of file
diff --git a/tests/Testcontainers.Temporal.Tests/TemporalContainerTest.cs b/tests/Testcontainers.Temporal.Tests/TemporalContainerTest.cs
new file mode 100644
index 000000000..c705bf1c3
--- /dev/null
+++ b/tests/Testcontainers.Temporal.Tests/TemporalContainerTest.cs
@@ -0,0 +1,69 @@
+namespace Testcontainers.Temporal;
+
+public sealed class TemporalContainerTest : IAsyncLifetime
+{
+ private readonly TemporalContainer _temporalContainer = new TemporalBuilder(TestSession.GetImageFromDockerfile()).Build();
+
+ public async ValueTask InitializeAsync()
+ {
+ await _temporalContainer.StartAsync()
+ .ConfigureAwait(false);
+ }
+
+ public ValueTask DisposeAsync()
+ {
+ return _temporalContainer.DisposeAsync();
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ public async Task ListNamespacesReturnsDefaultNamespace()
+ {
+ // Given
+ var clientOptions = new TemporalClientConnectOptions();
+ clientOptions.TargetHost = _temporalContainer.GetGrpcAddress();
+
+ var connectedClient = await TemporalClient.ConnectAsync(clientOptions)
+ .ConfigureAwait(true);
+
+ // When
+ var response = await connectedClient.WorkflowService.ListNamespacesAsync(new ListNamespacesRequest())
+ .ConfigureAwait(true);
+
+ // Then
+ Assert.Contains(response.Namespaces, ns => ns.NamespaceInfo.Name == clientOptions.Namespace);
+ Assert.Equal(_temporalContainer.GetGrpcAddress(), _temporalContainer.GetConnectionString());
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ public async Task DescribeWorkflowReturnsStartedWorkflow()
+ {
+ // Given
+ const string workflowType = "my-workflow";
+
+ var workflowOptions = new WorkflowOptions();
+ workflowOptions.Id = Guid.NewGuid().ToString("D");
+ workflowOptions.TaskQueue = Guid.NewGuid().ToString("D");
+ workflowOptions.Memo = new Dictionary { { "env", "test" } };
+
+ var clientOptions = new TemporalClientConnectOptions();
+ clientOptions.TargetHost = _temporalContainer.GetGrpcAddress();
+
+ var connectedClient = await TemporalClient.ConnectAsync(clientOptions)
+ .ConfigureAwait(true);
+
+ // When
+ var runningWorkflow = await connectedClient.StartWorkflowAsync(workflowType, Array.Empty