diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php
index 27a9064632..6669791fe0 100644
--- a/app/Actions/Database/StartRedis.php
+++ b/app/Actions/Database/StartRedis.php
@@ -108,8 +108,7 @@ class StartRedis
'labels' => defaultDatabaseLabels($this->database)->toArray(),
'healthcheck' => $this->database->healthCheckConfiguration([
'CMD-SHELL',
- 'redis-cli',
- 'ping',
+ $this->healthCheckCommand(),
]),
'mem_limit' => $this->database->limits_memory,
'memswap_limit' => $this->database->limits_memory_swap,
@@ -340,6 +339,20 @@ class StartRedis
return $keepsUnquotedPassword ? $this->resolvedRedisPassword : escapeshellarg($this->resolvedRedisPassword);
}
+ /**
+ * The healthcheck pings with the server password and requires PONG, because redis-cli exits 0 on
+ * error replies (NOAUTH, LOADING). A custom redis.conf with its own requirepass has a password that
+ * Coolify does not know, so that check only requires that the server replies.
+ */
+ private function healthCheckCommand(): string
+ {
+ if (str_contains((string) $this->database->redis_conf, 'requirepass')) {
+ return "redis-cli ping | grep -qE 'PONG|NOAUTH'";
+ }
+
+ return 'redis-cli -a '.$this->requirePassArgument().' --no-auth-warning ping | grep -q PONG';
+ }
+
private function add_custom_redis()
{
if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) {
diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php
index 7bb35ade98..40d0c794eb 100644
--- a/app/Http/Controllers/Api/DatabasesController.php
+++ b/app/Http/Controllers/Api/DatabasesController.php
@@ -1730,8 +1730,8 @@ class DatabasesController extends Controller
}
#[OA\Post(
- summary: 'Create (Redis)',
- description: 'Create a new Redis database.',
+ summary: 'Create (Redis / Valkey)',
+ description: 'Create a new Redis or Valkey database. Valkey (valkey/valkey:9-alpine) is the default. Set image to a Redis image (for example redis:8) for Redis.',
path: '/databases/redis',
operationId: 'create-database-redis',
security: [
@@ -1757,7 +1757,7 @@ class DatabasesController extends Controller
'redis_conf' => ['type' => 'string', 'description' => 'Redis conf'],
'name' => ['type' => 'string', 'description' => 'Name of the database'],
'description' => ['type' => 'string', 'description' => 'Description of the database'],
- 'image' => ['type' => 'string', 'description' => 'Docker Image of the database'],
+ 'image' => ['type' => 'string', 'description' => 'Docker image of the database. Default: valkey/valkey:9-alpine. For Redis, use a Redis image, for example redis:8.'],
'is_public' => ['type' => 'boolean', 'description' => 'Is the database public?'],
'public_port' => ['type' => 'integer', 'description' => 'Public port of the database'],
'public_port_timeout' => ['type' => 'integer', 'description' => 'Public port timeout in seconds (default: 3600)'],
diff --git a/app/Livewire/GlobalSearch.php b/app/Livewire/GlobalSearch.php
index fd32451bb7..193eff604f 100644
--- a/app/Livewire/GlobalSearch.php
+++ b/app/Livewire/GlobalSearch.php
@@ -207,6 +207,7 @@ class GlobalSearch extends Component
'new mysql' => 'mysql',
'new mariadb' => 'mariadb',
'new redis' => 'redis',
+ 'new valkey' => 'redis',
'new keydb' => 'keydb',
'new dragonfly' => 'dragonfly',
'new sqlite' => 'sqlite',
@@ -477,7 +478,7 @@ class GlobalSearch extends Component
'link' => $db->link(),
'project' => $db->environment->project->name ?? null,
'environment' => $db->environment->name ?? null,
- 'search_text' => strtolower($db->name.' '.$db->uuid.' redis '.$db->description.' database databases db'),
+ 'search_text' => strtolower($db->name.' '.$db->uuid.' redis valkey '.$db->description.' database databases db'),
];
})
);
@@ -1346,12 +1347,12 @@ class GlobalSearch extends Component
]);
$items->push([
- 'name' => 'Redis',
- 'description' => 'In-memory data structure store',
- 'quickcommand' => '(type: new redis)',
+ 'name' => 'Redis / Valkey',
+ 'description' => 'In-memory data structure store (Redis or Valkey)',
+ 'quickcommand' => '(type: new redis or new valkey)',
'type' => 'redis',
'category' => 'Databases',
- 'logo' => 'svgs/redis.svg',
+ 'logo' => 'svgs/resources/redis-valkey.svg',
'resourceType' => 'database',
]);
diff --git a/app/Livewire/Project/Database/Redis/StatusInfo.php b/app/Livewire/Project/Database/Redis/StatusInfo.php
index 2e784e2c09..ca1079285f 100644
--- a/app/Livewire/Project/Database/Redis/StatusInfo.php
+++ b/app/Livewire/Project/Database/Redis/StatusInfo.php
@@ -16,6 +16,6 @@ class StatusInfo extends Component
protected function databaseLabel(): string
{
- return 'Redis';
+ return 'Redis / Valkey';
}
}
diff --git a/app/Livewire/Project/New/Select.php b/app/Livewire/Project/New/Select.php
index ecc8ef0dd3..adf1b6cd31 100644
--- a/app/Livewire/Project/New/Select.php
+++ b/app/Livewire/Project/New/Select.php
@@ -38,6 +38,12 @@ class Select extends Component
public Collection|array $allServices = [];
+ /** Database types that ask for an image before the database is created. */
+ private const DATABASE_IMAGE_STEPS = [
+ 'postgresql' => 'select-postgresql-type',
+ 'redis' => 'select-redis-type',
+ ];
+
public bool $isDatabase = false;
public bool $includeSwarm = true;
@@ -50,8 +56,6 @@ class Select extends Component
public ?string $selectedEnvironment = null;
- public string $postgresql_type = 'postgres:18-alpine';
-
public ?string $existingPostgresqlUrl = null;
protected $queryString = [
@@ -72,20 +76,21 @@ class Select extends Component
$this->environments = $project->environments;
$this->selectedEnvironment = $this->environments->where('uuid', data_get($this->parameters, 'environment_uuid'))->firstOrFail()->name;
- // Check if we have all required params for PostgreSQL type selection
+ // Check if we have all required params for the database image step
// This handles navigation from global search
$queryType = request()->query('type');
$queryServerId = request()->query('server_id');
$queryDestination = request()->query('destination');
+ $imageStep = self::DATABASE_IMAGE_STEPS[$queryType] ?? null;
- if ($queryType === 'postgresql' && $queryServerId !== null && $queryDestination) {
+ if ($imageStep && $queryServerId !== null && $queryDestination) {
$server = Server::isUsable()->whereKey($queryServerId)->first();
if ($server && $this->findDestination($server, $queryDestination)) {
$this->type = $queryType;
$this->server_id = $server->id;
$this->destination_uuid = $queryDestination;
$this->server = $server;
- $this->current_step = 'select-postgresql-type';
+ $this->current_step = $imageStep;
}
}
} catch (\Exception $e) {
@@ -217,7 +222,7 @@ class Select extends Component
[
'id' => 'postgresql',
'name' => 'PostgreSQL',
- 'description' => 'A relational database with strong SQL standards support and extensibility.',
+ 'description' => 'An extensible relational database with strong SQL support.',
'logo' => resource_logo_url('postgres'),
],
[
@@ -235,9 +240,10 @@ class Select extends Component
],
[
'id' => 'redis',
- 'name' => 'Redis',
- 'description' => 'An in-memory key-value store used as a database, cache, and message broker.',
- 'logo' => resource_logo_url('redis'),
+ 'name' => 'Redis / Valkey',
+ 'description' => 'An in-memory key-value store for caching, queues, and sessions.',
+ 'logo' => resource_logo_url('redis-valkey'),
+ 'logoDark' => resource_logo_url('redis-valkey-dark'),
],
[
'id' => 'keydb',
@@ -256,13 +262,13 @@ class Select extends Component
[
'id' => 'mongodb',
'name' => 'MongoDB',
- 'description' => 'A document-oriented NoSQL database that stores JSON-like documents.',
+ 'description' => 'A NoSQL database that stores JSON-like documents.',
'logo' => resource_logo_url('mongodb'),
],
[
'id' => 'clickhouse',
'name' => 'ClickHouse',
- 'description' => 'A column-oriented database for real-time analytics over large datasets.',
+ 'description' => 'A column-oriented database for real-time analytics.',
'logo' => resource_logo_url('clickhouse'),
],
[
@@ -484,24 +490,22 @@ class Select extends Component
return $server->standaloneDockers->concat($server->swarmDockers)->firstWhere('uuid', $destinationUuid);
}
- public function setPostgresqlType(string $type)
+ public function setDatabaseImage(string $image)
{
- $this->postgresql_type = $type;
-
return redirect()->route('project.resource.create', [
'project_uuid' => $this->parameters['project_uuid'],
'environment_uuid' => $this->parameters['environment_uuid'],
'type' => $this->type,
'destination' => $this->destination_uuid,
'server_id' => $this->server_id,
- 'database_image' => $this->postgresql_type,
+ 'database_image' => $image,
]);
}
public function whatToDoNext()
{
- if ($this->type === 'postgresql') {
- $this->current_step = 'select-postgresql-type';
+ if (isset(self::DATABASE_IMAGE_STEPS[$this->type])) {
+ $this->current_step = self::DATABASE_IMAGE_STEPS[$this->type];
} else {
return redirect()->route('project.resource.create', [
'project_uuid' => $this->parameters['project_uuid'],
diff --git a/app/Livewire/Project/Resource/Create.php b/app/Livewire/Project/Resource/Create.php
index 12de2808f1..a0951330bd 100644
--- a/app/Livewire/Project/Resource/Create.php
+++ b/app/Livewire/Project/Resource/Create.php
@@ -51,9 +51,9 @@ class Create extends Component
}
$isDatabase = in_array($type, DATABASE_TYPES);
- // PostgreSQL requires database_image to be explicitly set
- // If not provided, fall through to Select component for version selection
- if ($isDatabase && $type->value() === 'postgresql' && ! $database_image) {
+ // PostgreSQL and Redis (Redis or Valkey) require database_image to be explicitly set
+ // If not provided, fall through to Select component for image selection
+ if ($isDatabase && in_array($type->value(), ['postgresql', 'redis'], true) && ! $database_image) {
$this->type = $type->value();
return;
@@ -98,7 +98,7 @@ class Create extends Component
databaseImage: $this->databaseImage
);
} elseif ($type->value() === 'redis') {
- $database = create_standalone_redis($environment->id, $destination);
+ $database = create_standalone_redis($environment->id, $destination, ['image' => $this->databaseImage]);
} elseif ($type->value() === 'mongodb') {
$database = create_standalone_mongodb($environment->id, $destination);
} elseif ($type->value() === 'mysql') {
diff --git a/app/Services/EnvironmentCanvas.php b/app/Services/EnvironmentCanvas.php
index 47ea3e87c4..c4acc84207 100644
--- a/app/Services/EnvironmentCanvas.php
+++ b/app/Services/EnvironmentCanvas.php
@@ -45,7 +45,7 @@ class EnvironmentCanvas
private const DATABASE_ENGINE_NAMES = [
'postgresql' => 'PostgreSQL',
- 'redis' => 'Redis',
+ 'redis' => 'Redis / Valkey',
'mongodb' => 'MongoDB',
'mysql' => 'MySQL',
'mariadb' => 'MariaDB',
diff --git a/database/migrations/2026_11_06_113843_set_valkey_as_standalone_redis_default_image.php b/database/migrations/2026_11_06_113843_set_valkey_as_standalone_redis_default_image.php
new file mode 100644
index 0000000000..175d5c80c6
--- /dev/null
+++ b/database/migrations/2026_11_06_113843_set_valkey_as_standalone_redis_default_image.php
@@ -0,0 +1,25 @@
+string('image')->default('valkey/valkey:9-alpine')->change();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('standalone_redis', function (Blueprint $table) {
+ $table->string('image')->default('redis:8')->change();
+ });
+ }
+};
diff --git a/openapi.json b/openapi.json
index cba7986122..0e44663be1 100644
--- a/openapi.json
+++ b/openapi.json
@@ -9053,8 +9053,8 @@
"tags": [
"Databases"
],
- "summary": "Create (Redis)",
- "description": "Create a new Redis database.",
+ "summary": "Create (Redis \/ Valkey)",
+ "description": "Create a new Redis or Valkey database. Valkey (valkey\/valkey:9-alpine) is the default. Set image to a Redis image (for example redis:8) for Redis.",
"operationId": "create-database-redis",
"requestBody": {
"description": "Database data",
@@ -9105,7 +9105,7 @@
},
"image": {
"type": "string",
- "description": "Docker Image of the database"
+ "description": "Docker image of the database. Default: valkey\/valkey:9-alpine. For Redis, use a Redis image, for example redis:8."
},
"is_public": {
"type": "boolean",
diff --git a/openapi.yaml b/openapi.yaml
index 8f713c76b4..6fd1432ce3 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -5937,8 +5937,8 @@ paths:
post:
tags:
- Databases
- summary: 'Create (Redis)'
- description: 'Create a new Redis database.'
+ summary: 'Create (Redis / Valkey)'
+ description: 'Create a new Redis or Valkey database. Valkey (valkey/valkey:9-alpine) is the default. Set image to a Redis image (for example redis:8) for Redis.'
operationId: create-database-redis
requestBody:
description: 'Database data'
@@ -5979,7 +5979,7 @@ paths:
description: 'Description of the database'
image:
type: string
- description: 'Docker Image of the database'
+ description: 'Docker image of the database. Default: valkey/valkey:9-alpine. For Redis, use a Redis image, for example redis:8.'
is_public:
type: boolean
description: 'Is the database public?'
diff --git a/public/svgs/resources/redis-valkey-dark.svg b/public/svgs/resources/redis-valkey-dark.svg
new file mode 100644
index 0000000000..1b70ab6132
--- /dev/null
+++ b/public/svgs/resources/redis-valkey-dark.svg
@@ -0,0 +1,24 @@
+
diff --git a/public/svgs/resources/redis-valkey.svg b/public/svgs/resources/redis-valkey.svg
new file mode 100644
index 0000000000..4dc437facd
--- /dev/null
+++ b/public/svgs/resources/redis-valkey.svg
@@ -0,0 +1,24 @@
+
diff --git a/resources/views/livewire/global-search.blade.php b/resources/views/livewire/global-search.blade.php
index ff4acf3d21..3d0e608c5c 100644
--- a/resources/views/livewire/global-search.blade.php
+++ b/resources/views/livewire/global-search.blade.php
@@ -264,7 +264,7 @@
'new private github', 'new private gh', 'new private deploy', 'new deploy key',
'new dockerfile', 'new docker compose', 'new compose', 'new docker image', 'new image',
'new postgresql', 'new postgres', 'new mysql', 'new mariadb',
- 'new redis', 'new keydb', 'new dragonfly', 'new mongodb', 'new mongo', 'new clickhouse', 'new sqlite'
+ 'new redis', 'new valkey', 'new keydb', 'new dragonfly', 'new mongodb', 'new mongo', 'new clickhouse', 'new sqlite'
];
if (this.scope === 'global' && exactMatchCommands.includes(trimmed)) {
const matchingItem = this.creatableItems.find(item => {
diff --git a/resources/views/livewire/project/database/redis/general.blade.php b/resources/views/livewire/project/database/redis/general.blade.php
index 2517ba86ee..ee9a78b989 100644
--- a/resources/views/livewire/project/database/redis/general.blade.php
+++ b/resources/views/livewire/project/database/redis/general.blade.php
@@ -3,7 +3,7 @@
Both work with the same clients, commands, + and configuration.
+