diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index c31dd05e048..f78595fff9e 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -68,6 +68,7 @@ body:
         - ToxiProxy
         - Trino
         - Typesense
+        - Valkey
         - Vault
         - Weaviate
         - YugabyteDB
diff --git a/.github/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/enhancement.yaml
index 9b9a06ecf6a..b63978775af 100644
--- a/.github/ISSUE_TEMPLATE/enhancement.yaml
+++ b/.github/ISSUE_TEMPLATE/enhancement.yaml
@@ -68,6 +68,7 @@ body:
         - ToxiProxy
         - Trino
         - Typesense
+        - Valkey
         - Vault
         - Weaviate
         - YugabyteDB
diff --git a/.github/ISSUE_TEMPLATE/feature.yaml b/.github/ISSUE_TEMPLATE/feature.yaml
index b655b4ac505..4a26337e90a 100644
--- a/.github/ISSUE_TEMPLATE/feature.yaml
+++ b/.github/ISSUE_TEMPLATE/feature.yaml
@@ -68,6 +68,7 @@ body:
         - ToxiProxy
         - Trino
         - Typesense
+        - Valkey
         - Vault
         - Weaviate
         - YugabyteDB
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 72a6d9110b6..7e84bc07e6e 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -373,6 +373,11 @@ updates:
     schedule:
       interval: "monthly"
     open-pull-requests-limit: 10
+  - package-ecosystem: "gradle"
+    directory: "/modules/valkey"
+    schedule:
+      interval: "monthly"
+    open-pull-requests-limit: 10
   - package-ecosystem: "gradle"
     directory: "/modules/vault"
     schedule:
diff --git a/.github/labeler.yml b/.github/labeler.yml
index f4649bd7f99..02efaf75f14 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -248,6 +248,10 @@
   - changed-files:
       - any-glob-to-any-file:
           - modules/typesense/**/*
+"modules/valkey":
+  - changed-files:
+      - any-glob-to-any-file:
+          - modules/valkey/**/*
 "modules/vault":
   - changed-files:
     - any-glob-to-any-file:
diff --git a/docs/modules/valkey.md b/docs/modules/valkey.md
new file mode 100644
index 00000000000..fd318cbcf3a
--- /dev/null
+++ b/docs/modules/valkey.md
@@ -0,0 +1,34 @@
+# Valkey
+
+!!! note This module is INCUBATING.
+While it is ready for use and operational in the current version of Testcontainers, it is possible that it may receive breaking changes in the future.
+See our [contributing guidelines](../contributing.md#incubating-modules) for more information on our incubating modules policy.
+
+Testcontainers module for [Valkey](https://hub.docker.com/r/valkey/valkey)
+
+## Valkey's usage examples
+
+You can start a Valkey container instance from any Java application by using:
+
+
+[Default Valkey container](../../modules/valkey/src/test/java/org/testcontainers/valkey/ValkeyContainerTest.java) inside_block:container
+
+
+## Adding this module to your project dependencies
+
+Add the following dependency to your `pom.xml`/`build.gradle` file:
+
+=== "Gradle"
+```groovy
+testImplementation "org.testcontainers:valkey:{{latest_version}}"
+```
+
+=== "Maven"
+```xml
+
+org.testcontainers
+valkey
+{{latest_version}}
+test
+
+```
diff --git a/mkdocs.yml b/mkdocs.yml
index 3e39a67f959..8dedfec2ede 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -108,6 +108,7 @@ nav:
           - modules/solr.md
           - modules/toxiproxy.md
           - modules/typesense.md
+          - modules/valkey.md
           - modules/vault.md
           - modules/weaviate.md
           - modules/webdriver_containers.md
diff --git a/modules/pinecone/build.gradle b/modules/pinecone/build.gradle
index 3ad5b97d98f..ad46d3ce9d9 100644
--- a/modules/pinecone/build.gradle
+++ b/modules/pinecone/build.gradle
@@ -1,4 +1,4 @@
-description = "Testcontainers :: ActiveMQ"
+description = "Testcontainers :: Pinecone"
 
 dependencies {
     api project(':testcontainers')
diff --git a/modules/valkey/build.gradle b/modules/valkey/build.gradle
new file mode 100644
index 00000000000..497b76a4c3f
--- /dev/null
+++ b/modules/valkey/build.gradle
@@ -0,0 +1,7 @@
+description = "Testcontainers :: Valkey"
+
+dependencies {
+    api project(':testcontainers')
+
+    testImplementation("io.valkey:valkey-java:5.5.0")
+}
diff --git a/modules/valkey/src/main/java/org/testcontainers/valkey/ValkeyContainer.java b/modules/valkey/src/main/java/org/testcontainers/valkey/ValkeyContainer.java
new file mode 100644
index 00000000000..e2ed4136e34
--- /dev/null
+++ b/modules/valkey/src/main/java/org/testcontainers/valkey/ValkeyContainer.java
@@ -0,0 +1,250 @@
+package org.testcontainers.valkey;
+
+import com.google.common.base.Preconditions;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.utility.DockerImageName;
+import org.testcontainers.utility.MountableFile;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Testcontainers implementation for Valkey.
+ * 
+ * Supported image: {@code valkey}
+ * 
+ * Exposed ports:
+ * 
+ */
+public class ValkeyContainer extends GenericContainer {
+
+    @AllArgsConstructor
+    @Getter
+    private static class SnapshottingSettings {
+
+        int seconds;
+
+        int changedKeys;
+    }
+
+    private static final DockerImageName DEFAULT_IMAGE = DockerImageName.parse("valkey/valkey:8.1");
+
+    private static final String DEFAULT_CONFIG_FILE = "/usr/local/valkey.conf";
+
+    private static final int CONTAINER_PORT = 6379;
+
+    private String username;
+
+    private String password;
+
+    private String persistenceVolume;
+
+    private String initialImportScriptFile;
+
+    private String configFile;
+
+    private ValkeyLogLevel logLevel;
+
+    private SnapshottingSettings snapshottingSettings;
+
+    public ValkeyContainer() {
+        this(DEFAULT_IMAGE);
+    }
+
+    public ValkeyContainer(String dockerImageName) {
+        this(DockerImageName.parse(dockerImageName));
+    }
+
+    public ValkeyContainer(DockerImageName dockerImageName) {
+        super(dockerImageName);
+        withExposedPorts(CONTAINER_PORT);
+        withStartupTimeout(Duration.ofMinutes(2));
+        waitingFor(Wait.forLogMessage(".*Ready to accept connections.*", 1));
+    }
+
+    public ValkeyContainer withUsername(String username) {
+        this.username = username;
+        return this;
+    }
+
+    public ValkeyContainer withPassword(String password) {
+        this.password = password;
+        return this;
+    }
+
+    /**
+     * Sets a host path to be mounted as a volume for Valkey persistence. The path must exist on the
+     * host system. Valkey will store its data in this directory.
+     */
+    public ValkeyContainer withPersistenceVolume(String persistenceVolume) {
+        this.persistenceVolume = persistenceVolume;
+        return this;
+    }
+
+    /**
+     * Sets an initial import script file to be executed via the Valkey CLI after startup.
+     * 
+     * Example line of an import script file: SET key1 "value1"
+     */
+    public ValkeyContainer withInitialData(String initialImportScriptFile) {
+        this.initialImportScriptFile = initialImportScriptFile;
+        return this;
+    }
+
+    /**
+     * Sets the log level for the valkey server process.
+     */
+    public ValkeyContainer withLogLevel(ValkeyLogLevel logLevel) {
+        this.logLevel = logLevel;
+        return this;
+    }
+
+    /**
+     * Sets the snapshotting configuration for the valkey server process. You can configure Valkey
+     * to have it save the dataset every N seconds if there are at least M changes in the dataset.
+     * This method allows Valkey to benefit from copy-on-write semantics.
+     *
+     * @see 
+     */
+    public ValkeyContainer withSnapshotting(int seconds, int changedKeys) {
+        Preconditions.checkArgument(seconds > 0, "seconds must be greater than 0");
+        Preconditions.checkArgument(changedKeys > 0, "changedKeys must be non-negative");
+
+        this.snapshottingSettings = new SnapshottingSettings(seconds, changedKeys);
+        return this;
+    }
+
+    /**
+     * Sets the config file to be used for the Valkey container.
+     */
+    public ValkeyContainer withConfigFile(String configFile) {
+        this.configFile = configFile;
+
+        return this;
+    }
+
+    @Override
+    public void start() {
+        List command = new ArrayList<>();
+        command.add("valkey-server");
+
+        if (StringUtils.isNotEmpty(configFile)) {
+            withCopyToContainer(MountableFile.forHostPath(configFile), DEFAULT_CONFIG_FILE);
+            command.add(DEFAULT_CONFIG_FILE);
+        }
+
+        if (StringUtils.isNotEmpty(password)) {
+            command.add("--requirepass");
+            command.add(password);
+
+            if (StringUtils.isNotEmpty(username)) {
+                command.add("--user " + username + " on >" + password + " ~* +@all");
+            }
+        }
+
+        if (StringUtils.isNotEmpty(persistenceVolume)) {
+            command.addAll(Arrays.asList("--appendonly", "yes"));
+            withFileSystemBind(persistenceVolume, "/data");
+        }
+
+        if (snapshottingSettings != null) {
+            command.addAll(
+                Arrays.asList("--save",
+                    snapshottingSettings.getSeconds() + " " + snapshottingSettings.getChangedKeys())
+            );
+        }
+
+        if (logLevel != null) {
+            command.addAll(Arrays.asList("--loglevel", logLevel.getLevel()));
+        }
+
+        if (StringUtils.isNotEmpty(initialImportScriptFile)) {
+            withCopyToContainer(MountableFile.forHostPath(initialImportScriptFile),
+                "/tmp/import.valkey");
+            withCopyToContainer(MountableFile.forClasspathResource("import.sh"), "/tmp/import.sh");
+        }
+
+        withCommand(command.toArray(new String[0]));
+
+        super.start();
+
+        evaluateImportScript();
+    }
+
+    public int getPort() {
+        return getMappedPort(CONTAINER_PORT);
+    }
+
+    /**
+     * Executes a command in the Valkey CLI inside the container.
+     */
+    public String executeCli(String cmd, String... flags) {
+        List args = new ArrayList<>();
+        args.add("redis-cli");
+
+        if (StringUtils.isNotEmpty(password)) {
+            args.addAll(
+                StringUtils.isNotEmpty(username)
+                    ? Arrays.asList("--user", username, "--pass", password)
+                    : Arrays.asList("--pass", password)
+            );
+        }
+
+        args.add(cmd);
+        args.addAll(Arrays.asList(flags));
+
+        try {
+            ExecResult result = execInContainer(args.toArray(new String[0]));
+            if (result.getExitCode() != 0) {
+                throw new RuntimeException(result.getStdout() + result.getStderr());
+            }
+
+            return result.getStdout();
+        } catch (Exception e) {
+            throw new RuntimeException("failed to execute CLI command", e);
+        }
+    }
+
+    public String createConnectionUrl() {
+        String userInfo = null;
+        if (StringUtils.isNotEmpty(username) && StringUtils.isNotEmpty(password)) {
+            userInfo = username + ":" + password;
+        } else if (StringUtils.isNotEmpty(password)) {
+            userInfo = ":" + password;
+        }
+
+        try {
+            URI uri = new URI("redis", userInfo, getHost(), getPort(), null, null, null);
+            return uri.toString();
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("Failed to build Redis URI", e);
+        }
+    }
+
+    private void evaluateImportScript() {
+        if (StringUtils.isEmpty(initialImportScriptFile)) {
+            return;
+        }
+
+        try {
+            ExecResult result = execInContainer("/bin/sh", "/tmp/import.sh",
+                password != null ? password : "");
+
+            if (result.getExitCode() != 0 || result.getStdout().contains("ERR")) {
+                throw new RuntimeException("Could not import initial data: " + result.getStdout());
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/modules/valkey/src/main/java/org/testcontainers/valkey/ValkeyLogLevel.java b/modules/valkey/src/main/java/org/testcontainers/valkey/ValkeyLogLevel.java
new file mode 100644
index 00000000000..b24884d66c4
--- /dev/null
+++ b/modules/valkey/src/main/java/org/testcontainers/valkey/ValkeyLogLevel.java
@@ -0,0 +1,18 @@
+package org.testcontainers.valkey;
+
+public enum ValkeyLogLevel {
+    DEBUG("debug"),
+    VERBOSE("verbose"),
+    NOTICE("notice"),
+    WARNING("warning");
+
+    private final String level;
+
+    ValkeyLogLevel(String level) {
+        this.level = level;
+    }
+
+    public String getLevel() {
+        return level;
+    }
+}
diff --git a/modules/valkey/src/main/resources/import.sh b/modules/valkey/src/main/resources/import.sh
new file mode 100644
index 00000000000..bfc76a22606
--- /dev/null
+++ b/modules/valkey/src/main/resources/import.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+set -e
+valkey-cli $([[ -n "$1" ]] && echo "-a $1") < "/tmp/import.valkey"
+echo "Imported"
diff --git a/modules/valkey/src/test/java/org/testcontainers/valkey/ValkeyContainerTest.java b/modules/valkey/src/test/java/org/testcontainers/valkey/ValkeyContainerTest.java
new file mode 100644
index 00000000000..2a93c677e84
--- /dev/null
+++ b/modules/valkey/src/test/java/org/testcontainers/valkey/ValkeyContainerTest.java
@@ -0,0 +1,144 @@
+package org.testcontainers.valkey;
+
+import io.valkey.Jedis;
+import io.valkey.JedisPool;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class ValkeyContainerTest {
+
+    @TempDir
+    Path tempDir;
+
+    @Test
+    void shouldWriteAndReadEntry() {
+        try (
+            ValkeyContainer valkeyContainer = new ValkeyContainer()
+                .withLogLevel(ValkeyLogLevel.DEBUG)
+                .withSnapshotting(3, 1)
+        ) {
+            valkeyContainer.start();
+            try (JedisPool jedisPool = new JedisPool(valkeyContainer.createConnectionUrl());
+                Jedis jedis = jedisPool.getResource()) {
+                jedis.set("key", "value");
+                assertThat(jedis.get("key")).isEqualTo("value");
+            }
+        }
+    }
+
+    @Test
+    void shouldConfigureServiceWithAuthentication() {
+        try (
+            ValkeyContainer valkeyContainer = new ValkeyContainer().withUsername("testuser")
+                .withPassword("testpass")
+        ) {
+            valkeyContainer.start();
+            String url = valkeyContainer.createConnectionUrl();
+            assertThat(url).contains("testuser:testpass");
+
+            try (JedisPool jedisPool = new JedisPool(url);
+                Jedis jedis = jedisPool.getResource()) {
+                jedis.set("k1", "v2");
+                assertThat(jedis.get("k1")).isEqualTo("v2");
+            }
+        }
+    }
+
+
+    @Test
+    void shouldPersistData() {
+        Path dataDir = tempDir.resolve("valkey-data");
+        dataDir.toFile().mkdirs();
+
+        try (
+            ValkeyContainer valkeyContainer = new ValkeyContainer()
+                .withPersistenceVolume(dataDir.toString())
+                .withSnapshotting(1, 1)
+        ) {
+            valkeyContainer.start();
+
+            String containerConnectionUrl = valkeyContainer.createConnectionUrl();
+            try (JedisPool jedisPool = new JedisPool(containerConnectionUrl);
+                Jedis jedis = jedisPool.getResource()) {
+                jedis.set("persistKey", "persistValue");
+            }
+
+            valkeyContainer.stop();
+            try (ValkeyContainer restarted = new ValkeyContainer().withPersistenceVolume(
+                dataDir.toString())) {
+                restarted.start();
+                String connectionUrl = restarted.createConnectionUrl();
+
+                try (JedisPool restartedPool = new JedisPool(connectionUrl);
+                    Jedis jedis = restartedPool.getResource()) {
+                    assertThat(jedis.get("persistKey")).isEqualTo("persistValue");
+                }
+            }
+        }
+    }
+
+    @Test
+    void shouldInitializeDatabaseWithPayload() throws Exception {
+        Path importFile = Paths.get(getClass().getResource("/initData.valkey").toURI());
+
+        try (ValkeyContainer valkeyContainer = new ValkeyContainer().withInitialData(
+            importFile.toString())) {
+            valkeyContainer.start();
+            String connectionUrl = valkeyContainer.createConnectionUrl();
+
+            try (JedisPool jedisPool = new JedisPool(
+                connectionUrl); Jedis jedis = jedisPool.getResource()) {
+                assertThat(jedis.get("key1")).isEqualTo("value1");
+                assertThat(jedis.get("key2")).isEqualTo("value2");
+            }
+        }
+    }
+
+    @Test
+    void shouldExecuteContainerCmdAndReturnResult() {
+        try (ValkeyContainer valkeyContainer = new ValkeyContainer()) {
+            valkeyContainer.start();
+
+            String queryResult = valkeyContainer.executeCli("info", "clients");
+
+            assertThat(queryResult).contains("connected_clients:1");
+        }
+    }
+
+    @Test
+    void shouldMountValkeyConfigToContainer() throws Exception {
+        Path configFile = Paths.get(getClass().getResource("/valkey.conf").toURI());
+
+        try (ValkeyContainer valkeyContainer = new ValkeyContainer().withConfigFile(
+            configFile.toString())) {
+            valkeyContainer.start();
+
+            String connectionUrl = valkeyContainer.createConnectionUrl();
+            try (JedisPool jedisPool = new JedisPool(connectionUrl);
+                Jedis jedis = jedisPool.getResource()) {
+                String maxMemory = jedis.configGet("maxmemory").get("maxmemory");
+
+                assertThat(maxMemory).isEqualTo("2097152");
+            }
+        }
+    }
+
+    @Test
+    void shouldValidateSnapshottingConfiguration() {
+        try (ValkeyContainer container = new ValkeyContainer()) {
+            assertThatThrownBy(() -> container.withSnapshotting(0, 10))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("seconds must be greater than 0");
+
+            assertThatThrownBy(() -> container.withSnapshotting(10, 0))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("changedKeys must be non-negative");
+        }
+    }
+}
diff --git a/modules/valkey/src/test/resources/initData.valkey b/modules/valkey/src/test/resources/initData.valkey
new file mode 100644
index 00000000000..e2c4c2c8e7b
--- /dev/null
+++ b/modules/valkey/src/test/resources/initData.valkey
@@ -0,0 +1,2 @@
+SET key1 "value1"
+SET key2 "value2"
diff --git a/modules/valkey/src/test/resources/valkey.conf b/modules/valkey/src/test/resources/valkey.conf
new file mode 100644
index 00000000000..b58609fc4b3
--- /dev/null
+++ b/modules/valkey/src/test/resources/valkey.conf
@@ -0,0 +1 @@
+maxmemory 2mb