diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml
index e12f947b2..c87505494 100644
--- a/.github/workflows/cicd.yml
+++ b/.github/workflows/cicd.yml
@@ -92,6 +92,7 @@ jobs:
{ name: "Testcontainers.ServiceBus", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Sftp", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Typesense", runs-on: "ubuntu-22.04" },
+ { name: "Testcontainers.Valkey", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Weaviate", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.WebDriver", runs-on: "ubuntu-22.04" },
{ name: "Testcontainers.Xunit", runs-on: "ubuntu-22.04" },
diff --git a/Testcontainers.sln b/Testcontainers.sln
index e09e2b093..7c12bf125 100644
--- a/Testcontainers.sln
+++ b/Testcontainers.sln
@@ -111,6 +111,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redis", "src
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redpanda", "src\Testcontainers.Redpanda\Testcontainers.Redpanda.csproj", "{45D6F69C-4D87-4130-AA90-0DB2F7460DAE}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Valkey", "src\Testcontainers.Valkey\Testcontainers.Valkey.csproj", "{E91FFC43-082A-42DA-AF40-97C49D4F8C3C}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus", "src\Testcontainers.ServiceBus\Testcontainers.ServiceBus.csproj", "{2E39E532-B81E-4B48-A004-FAE18EDF9E79}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Sftp", "src\Testcontainers.Sftp\Testcontainers.Sftp.csproj", "{7D5C6816-0DD2-4E13-A585-033B5D3C80D5}"
@@ -239,6 +241,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redis.Tests"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Redpanda.Tests", "tests\Testcontainers.Redpanda.Tests\Testcontainers.Redpanda.Tests.csproj", "{867BD04E-4670-4FBA-98D5-9F83220E6DFB}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Valkey.Tests", "tests\Testcontainers.Valkey.Tests\Testcontainers.Valkey.Tests.csproj", "{7A1885A8-291B-49D4-81A6-5644281C5A6E}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ResourceReaper.Tests", "tests\Testcontainers.ResourceReaper.Tests\Testcontainers.ResourceReaper.Tests.csproj", "{9E8E6AA5-65D1-498F-BEAB-BA34723A0050}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.ServiceBus.Tests", "tests\Testcontainers.ServiceBus.Tests\Testcontainers.ServiceBus.Tests.csproj", "{232DD918-46ED-4BA8-B383-1A9146D83064}"
@@ -459,6 +463,10 @@ Global
{45D6F69C-4D87-4130-AA90-0DB2F7460DAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45D6F69C-4D87-4130-AA90-0DB2F7460DAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45D6F69C-4D87-4130-AA90-0DB2F7460DAE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E91FFC43-082A-42DA-AF40-97C49D4F8C3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E91FFC43-082A-42DA-AF40-97C49D4F8C3C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E91FFC43-082A-42DA-AF40-97C49D4F8C3C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E91FFC43-082A-42DA-AF40-97C49D4F8C3C}.Release|Any CPU.Build.0 = Release|Any CPU
{2E39E532-B81E-4B48-A004-FAE18EDF9E79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E39E532-B81E-4B48-A004-FAE18EDF9E79}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E39E532-B81E-4B48-A004-FAE18EDF9E79}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -715,6 +723,10 @@ Global
{867BD04E-4670-4FBA-98D5-9F83220E6DFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{867BD04E-4670-4FBA-98D5-9F83220E6DFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{867BD04E-4670-4FBA-98D5-9F83220E6DFB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7A1885A8-291B-49D4-81A6-5644281C5A6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7A1885A8-291B-49D4-81A6-5644281C5A6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7A1885A8-291B-49D4-81A6-5644281C5A6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7A1885A8-291B-49D4-81A6-5644281C5A6E}.Release|Any CPU.Build.0 = Release|Any CPU
{9E8E6AA5-65D1-498F-BEAB-BA34723A0050}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E8E6AA5-65D1-498F-BEAB-BA34723A0050}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E8E6AA5-65D1-498F-BEAB-BA34723A0050}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -805,6 +817,7 @@ Global
{F6394475-D6F1-46E2-81BF-4BA78A40B878} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{BFDA179A-40EB-4CEB-B8E9-0DF32C65E2C5} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{45D6F69C-4D87-4130-AA90-0DB2F7460DAE} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
+ {E91FFC43-082A-42DA-AF40-97C49D4F8C3C} = {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}
{E044A94A-3081-4EE4-8DC6-81601F96DA14} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
@@ -869,6 +882,7 @@ Global
{D53726B6-5447-47E6-B881-A44EFF6E5534} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{31EE94A0-E721-4073-B6F1-DD912D004DEF} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{867BD04E-4670-4FBA-98D5-9F83220E6DFB} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
+ {7A1885A8-291B-49D4-81A6-5644281C5A6E} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{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}
diff --git a/docs/modules/index.md b/docs/modules/index.md
index 254a2d9fe..2043b2f24 100644
--- a/docs/modules/index.md
+++ b/docs/modules/index.md
@@ -71,6 +71,7 @@ await moduleNameContainer.StartAsync();
| Sftp | `atmoz/sftp:alpine` | [NuGet](https://www.nuget.org/packages/Testcontainers.Sftp) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Sftp) |
| SQL Server | `mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04` | [NuGet](https://www.nuget.org/packages/Testcontainers.MsSql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MsSql) |
| Typesense | `typesense/typesense:28.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.Typesense) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Typesense) |
+| Valkey | `valkey/valkey:8.0-alpine` | [NuGet](https://www.nuget.org/packages/Testcontainers.Valkey) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Valkey) |
| Weaviate | `semitechnologies/weaviate:1.26.14` | [NuGet](https://www.nuget.org/packages/Testcontainers.Weaviate) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Weaviate) |
| WebDriver | `selenium/standalone-chrome:110.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.WebDriver) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.WebDriver) |
diff --git a/docs/modules/valkey.md b/docs/modules/valkey.md
new file mode 100644
index 000000000..ddd611a15
--- /dev/null
+++ b/docs/modules/valkey.md
@@ -0,0 +1,52 @@
+# Valkey
+
+[Valkey](https://valkey.io/) is an open-source, high-performance data structure server that serves as a drop-in replacement for Redis. It supports various data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams.
+
+Add the following dependency to your project file:
+
+```shell title="NuGet"
+dotnet add package Testcontainers.Valkey
+```
+
+You can start a Valkey container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations.
+
+=== "Create Container Instance"
+ ```csharp
+ --8<-- "tests/Testcontainers.Valkey.Tests/ValkeyContainerTest.cs:CreateValkeyContainer"
+ ```
+
+This example uses xUnit.net's `IAsyncLifetime` interface to manage the lifecycle of the container. The container is started in the `InitializeAsync` method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the `DisposeAsync` method.
+
+=== "Usage Example"
+ ```csharp
+ --8<-- "tests/Testcontainers.Valkey.Tests/ValkeyContainerTest.cs:UseValkeyContainer"
+ ```
+
+The test example uses the following NuGet dependencies:
+
+=== "Package References"
+ ```xml
+ --8<-- "tests/Testcontainers.Valkey.Tests/Testcontainers.Valkey.Tests.csproj:PackageReferences"
+ ```
+
+To execute the tests, use the command `dotnet test` from a terminal.
+
+--8<-- "docs/modules/_call_out_test_projects.txt"
+
+## Connection String
+
+The Valkey module provides a `GetConnectionString()` method that returns a connection string compatible with StackExchange.Redis and other Redis client libraries that support Valkey:
+
+```csharp
+var connectionString = _valkeyContainer.GetConnectionString();
+using var connection = await ConnectionMultiplexer.ConnectAsync(connectionString);
+```
+
+## Executing Scripts
+
+You can execute Lua scripts against the Valkey container using the standard Redis client libraries:
+
+```csharp
+const string scriptContent = "return 'Hello, Valkey!'";
+var execResult = await _valkeyContainer.ExecScriptAsync(scriptContent);
+```
\ No newline at end of file
diff --git a/src/Testcontainers.Valkey/Testcontainers.Valkey.csproj b/src/Testcontainers.Valkey/Testcontainers.Valkey.csproj
new file mode 100644
index 000000000..9a25b9c4d
--- /dev/null
+++ b/src/Testcontainers.Valkey/Testcontainers.Valkey.csproj
@@ -0,0 +1,12 @@
+
+
+ net8.0;net9.0;netstandard2.0;netstandard2.1
+ latest
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Testcontainers.Valkey/Usings.cs b/src/Testcontainers.Valkey/Usings.cs
new file mode 100644
index 000000000..6fe3559a7
--- /dev/null
+++ b/src/Testcontainers.Valkey/Usings.cs
@@ -0,0 +1,11 @@
+global using System;
+global using System.IO;
+global using System.Text;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using Docker.DotNet.Models;
+global using DotNet.Testcontainers;
+global using DotNet.Testcontainers.Builders;
+global using DotNet.Testcontainers.Configurations;
+global using DotNet.Testcontainers.Containers;
+global using JetBrains.Annotations;
\ No newline at end of file
diff --git a/src/Testcontainers.Valkey/ValkeyBuilder.cs b/src/Testcontainers.Valkey/ValkeyBuilder.cs
new file mode 100644
index 000000000..a37d9ee96
--- /dev/null
+++ b/src/Testcontainers.Valkey/ValkeyBuilder.cs
@@ -0,0 +1,66 @@
+namespace Testcontainers.Valkey;
+
+///
+[PublicAPI]
+public sealed class ValkeyBuilder : ContainerBuilder
+{
+ public const string ValkeyImage = "valkey/valkey:8.1";
+
+ public const ushort ValkeyPort = 6379;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ValkeyBuilder()
+ : this(new ValkeyConfiguration())
+ {
+ DockerResourceConfiguration = Init().DockerResourceConfiguration;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ private ValkeyBuilder(ValkeyConfiguration resourceConfiguration)
+ : base(resourceConfiguration)
+ {
+ DockerResourceConfiguration = resourceConfiguration;
+ }
+
+ ///
+ protected override ValkeyConfiguration DockerResourceConfiguration { get; }
+
+ ///
+ public override ValkeyContainer Build()
+ {
+ Validate();
+ return new ValkeyContainer(DockerResourceConfiguration);
+ }
+
+ ///
+ protected override ValkeyBuilder Init()
+ {
+ return base.Init()
+ .WithImage(ValkeyImage)
+ .WithPortBinding(ValkeyPort, true)
+ .WithWaitStrategy(Wait.ForUnixContainer().UntilMessageIsLogged("Ready to accept connections"));
+ }
+
+ ///
+ protected override ValkeyBuilder Clone(IResourceConfiguration resourceConfiguration)
+ {
+ return Merge(DockerResourceConfiguration, new ValkeyConfiguration(resourceConfiguration));
+ }
+
+ ///
+ protected override ValkeyBuilder Clone(IContainerConfiguration resourceConfiguration)
+ {
+ return Merge(DockerResourceConfiguration, new ValkeyConfiguration(resourceConfiguration));
+ }
+
+ ///
+ protected override ValkeyBuilder Merge(ValkeyConfiguration oldValue, ValkeyConfiguration newValue)
+ {
+ return new ValkeyBuilder(new ValkeyConfiguration(oldValue, newValue));
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Valkey/ValkeyConfiguration.cs b/src/Testcontainers.Valkey/ValkeyConfiguration.cs
new file mode 100644
index 000000000..8c0f8308b
--- /dev/null
+++ b/src/Testcontainers.Valkey/ValkeyConfiguration.cs
@@ -0,0 +1,53 @@
+namespace Testcontainers.Valkey;
+
+///
+[PublicAPI]
+public sealed class ValkeyConfiguration : ContainerConfiguration
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ValkeyConfiguration()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Docker resource configuration.
+ public ValkeyConfiguration(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 ValkeyConfiguration(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 ValkeyConfiguration(ValkeyConfiguration resourceConfiguration)
+ : this(new ValkeyConfiguration(), 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 ValkeyConfiguration(ValkeyConfiguration oldValue, ValkeyConfiguration newValue)
+ : base(oldValue, newValue)
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/Testcontainers.Valkey/ValkeyContainer.cs b/src/Testcontainers.Valkey/ValkeyContainer.cs
new file mode 100644
index 000000000..c7067b719
--- /dev/null
+++ b/src/Testcontainers.Valkey/ValkeyContainer.cs
@@ -0,0 +1,41 @@
+namespace Testcontainers.Valkey;
+
+///
+[PublicAPI]
+public sealed class ValkeyContainer : DockerContainer
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The container configuration.
+ public ValkeyContainer(ValkeyConfiguration configuration)
+ : base(configuration)
+ {
+ }
+
+ ///
+ /// Gets the Valkey connection string.
+ ///
+ /// The Valkey connection string.
+ public string GetConnectionString()
+ {
+ return new UriBuilder("valkey", Hostname, GetMappedPublicPort(ValkeyBuilder.ValkeyPort)).Uri.Authority;
+ }
+
+ ///
+ /// Executes the Lua script in the Valkey container.
+ ///
+ /// The content of the Lua script to execute.
+ /// Cancellation token.
+ /// Task that completes when the Lua script has been executed.
+ public async Task ExecScriptAsync(string scriptContent, CancellationToken ct = default)
+ {
+ var scriptFilePath = string.Join("/", string.Empty, "tmp", Guid.NewGuid().ToString("D"), Path.GetRandomFileName());
+
+ await CopyAsync(Encoding.Default.GetBytes(scriptContent), scriptFilePath, Unix.FileMode644, ct)
+ .ConfigureAwait(false);
+
+ return await ExecAsync(new[] { "valkey-cli", "--eval", scriptFilePath, "0" }, ct)
+ .ConfigureAwait(false);
+ }
+}
\ No newline at end of file
diff --git a/tests/Testcontainers.Valkey.Tests/Testcontainers.Valkey.Tests.csproj b/tests/Testcontainers.Valkey.Tests/Testcontainers.Valkey.Tests.csproj
new file mode 100644
index 000000000..9ad23adf1
--- /dev/null
+++ b/tests/Testcontainers.Valkey.Tests/Testcontainers.Valkey.Tests.csproj
@@ -0,0 +1,19 @@
+
+
+ net9.0
+ false
+ false
+ Exe
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/Testcontainers.Valkey.Tests/Usings.cs b/tests/Testcontainers.Valkey.Tests/Usings.cs
new file mode 100644
index 000000000..4e7182108
--- /dev/null
+++ b/tests/Testcontainers.Valkey.Tests/Usings.cs
@@ -0,0 +1,4 @@
+global using System.Threading.Tasks;
+global using DotNet.Testcontainers.Commons;
+global using StackExchange.Redis;
+global using Xunit;
\ No newline at end of file
diff --git a/tests/Testcontainers.Valkey.Tests/ValkeyContainerTest.cs b/tests/Testcontainers.Valkey.Tests/ValkeyContainerTest.cs
new file mode 100644
index 000000000..c5aa69817
--- /dev/null
+++ b/tests/Testcontainers.Valkey.Tests/ValkeyContainerTest.cs
@@ -0,0 +1,62 @@
+namespace Testcontainers.Valkey;
+
+public sealed class ValkeyContainerTest : IAsyncLifetime
+{
+ private readonly ValkeyContainer _valkeyContainer = new ValkeyBuilder().Build();
+
+ public async ValueTask InitializeAsync()
+ {
+ await _valkeyContainer.StartAsync()
+ .ConfigureAwait(false);
+ }
+
+ public ValueTask DisposeAsync()
+ {
+ return _valkeyContainer.DisposeAsync();
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ public async Task ConnectionStateReturnsOpen()
+ {
+ using var connection = await ConnectionMultiplexer.ConnectAsync(_valkeyContainer.GetConnectionString());
+ Assert.True(connection.IsConnected);
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ public async Task Can_Set_And_Retrieve_Key()
+ {
+ using var connection = await ConnectionMultiplexer.ConnectAsync(_valkeyContainer.GetConnectionString());
+
+ var db = connection.GetDatabase();
+ const string key = "test-key";
+ const string value = "test-value";
+
+ var redisValue = await db.StringGetAsync(key);
+ Assert.True(redisValue.IsNull);
+
+ await db.StringSetAsync(key, value);
+
+ var updatedValue = await db.StringGetAsync(key);
+
+ Assert.Equal(value, updatedValue);
+ }
+
+ [Fact]
+ [Trait(nameof(DockerCli.DockerPlatform), nameof(DockerCli.DockerPlatform.Linux))]
+ public async Task ExecScriptReturnsSuccessful()
+ {
+ // Given
+ const string scriptContent = "return 'Hello, Valkey!'";
+
+ // When
+ var execResult = await _valkeyContainer.ExecScriptAsync(scriptContent, TestContext.Current.CancellationToken)
+ .ConfigureAwait(true);
+
+ // Then
+ Assert.True(0L.Equals(execResult.ExitCode), execResult.Stderr);
+ Assert.True("Hello, Valkey!\n".Equals(execResult.Stdout), execResult.Stdout);
+ Assert.Empty(execResult.Stderr);
+ }
+}