> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sevalla.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These are the docs for Sevalla, a platform for deploying applications, databases, static sites, object storage and load balancers. The dashboard is at https://app.sevalla.com.
> Bold text in steps is an exact dashboard label.
> To act on a Sevalla account, prefer the Sevalla MCP server (https://mcp.sevalla.com/mcp, see https://docs.sevalla.com/ai-agents/overview.md), then the Sevalla CLI (https://docs.sevalla.com/developer-tools/cli.md), then the REST API (https://api-docs.sevalla.com, OpenAPI spec at https://api.sevalla.com/v3/openapi.json).
> Every page index is at https://docs.sevalla.com/llms.txt.

# Cache Laravel with Redis

> Use a Sevalla Redis or Valkey database as Laravel's cache and session store over an internal connection, cache values with Cache::remember, and check that they come from the cache.

Laravel 13 keeps its cache in the database by default (`CACHE_STORE` is `database`). With a Sevalla Redis or Valkey database as the cache store, cache reads and writes move out of PostgreSQL into memory, and every instance of the web process shares one cache. Sessions can move there too.

## At a glance

What this guide adds to the application from [Deploy a Laravel application](https://docs.sevalla.com/guides/php/laravel/deploy.md#at-a-glance):

| | |
| - | - |
| Services | A Redis or Valkey database over an internal connection, next to the deploy guide's PostgreSQL |
| Size | **DB1** for the Redis database |

### Environment variables

| Variable | Required | Available during | Secret | Value |
| - | - | - | - | - |
| `CACHE_STORE` | Yes | Runtime | No | `redis` |
| `SESSION_DRIVER` | No | Runtime | No | `redis` to keep sessions in Redis. Without it, Laravel 13 uses `database`. |
| `REDIS_HOST` | Yes | Runtime | No | Added by the internal connection: the database's internal hostname |
| `REDIS_PORT` | Yes | Runtime | No | Added by the internal connection: `6379` |
| `REDIS_PASSWORD` | Yes | Runtime | Yes | Added by the internal connection: the database password |
| `REDIS_URL` | No | Runtime | Yes | Added by the internal connection: `redis://:<password>@<host>:<port>/0`, or `valkey://` for Valkey |

The application's other variables are in [Deploy a Laravel application](https://docs.sevalla.com/guides/php/laravel/deploy.md#environment-variables).

## Before you begin

* A Laravel application deployed on Sevalla, as in [Deploy Laravel](https://docs.sevalla.com/guides/php/laravel/deploy.md).

## Create a Redis database

**1. Create the database**

In the sidebar, click **Databases**, then click **Create database**. Under **Database**, choose **Redis**, or **Valkey**: this guide works with both. Enter `storefront-redis` as the **Display name**, choose the same **Data center** as the application and a **Size**, then click **Create database**.

**2. Connect it to the application**

In the sidebar, click **Applications**, then click the application. Click the **Networking** tab. Under **Connected services**, click **Add internal connection**. Choose `storefront-redis` as the **Service**, turn on **Add environment variables to the application**, and click **Add internal connection**. Sevalla adds `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD` and `REDIS_URL`, the names Laravel's `config/database.php` reads. See [Connect an application](https://docs.sevalla.com/databases/connect.md#connect-an-application).

**3. Use Redis for the cache**

Click the **Environment variables** tab, then click **Add environment variables**. Add `CACHE_STORE` with the value `redis`, and click **Add environment variables**.

![The Add internal connection sheet with the Service field and the Add environment variables to the application switch](https://assets.sevalla.com/screenshots/light/app-networking-internal-connection-add.png)

Laravel talks to Redis with its default client, `phpredis`, the `redis` PHP extension, which Ingot installs for every Laravel application. It reads the `valkey://` URL that Sevalla adds for a Valkey database too.

The other client, Predis, is a Composer package that needs no extension. Use it when your image doesn't have the extension: install it with `composer require predis/predis` and set `REDIS_CLIENT` to `predis`.

## Cache a value

`Cache::remember` returns the value stored under a key, or runs the closure, stores what it returns for the number of seconds you give, and returns it. Cache a count for 2 minutes:

```php routes/web.php theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
<?php

use App\Models\User;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/stats', function () {
    return Cache::remember('stats', 120, fn () => [
        'users' => User::count(),
        'generated_at' => now()->toDateTimeString(),
    ]);
});
```

Commit and push the change. If **Auto-deploy** is off, click **Deploy now** on the application's **Overview** tab. `CACHE_STORE` applies with the same deployment.

## Store sessions in Redis

Laravel 13 keeps sessions in the `sessions` table by default. To keep them in Redis, add the `SESSION_DRIVER` environment variable with the value `redis`, and deploy. Sessions saved in the database stay there, so visitors who were signed in sign in again once.

## Update the Dockerfile

If the application builds from the [Dockerfile](https://docs.sevalla.com/guides/php/laravel/deploy.md#deploy-with-a-dockerfile) in the deploy guide, install the `redis` extension, which Ingot installs on its own. Change the first `RUN` line:

```dockerfile Dockerfile theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
RUN install-php-extensions pdo_pgsql redis \
```

The `ldd` line finds the libraries the extension loads, so the final stage installs them with no other change.

## Verify

When the deployment's status is **Deployed**, request the page twice, with your application's domain:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl https://storefront-x7k2m.sevalla.app/stats
curl https://storefront-x7k2m.sevalla.app/stats
```

Both responses show the same time, for example `{"users":0,"generated_at":"2026-10-29 19:14:58"}`: the second one came from the cache. After 2 minutes, the next request shows a new time. To look at the keys, open the database and click the **Redis studio** tab: the cached value is under `laravel-database-laravel-cache-stats`, Laravel's Redis prefix followed by its cache prefix and the key. With sessions in Redis, each session is a key that starts with `laravel-database-laravel-cache-` too, followed by the session ID. See [Database studio](https://docs.sevalla.com/databases/studio.md).

To cache whole responses at Cloudflare's edge, in front of the application, see [CDN and edge caching](https://docs.sevalla.com/applications/caching.md).

### Check from a terminal

These checks need no dashboard. Request `/stats` twice, a few seconds apart, with your domain:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s https://storefront-x7k2m.sevalla.app/stats; echo
sleep 5
curl -s https://storefront-x7k2m.sevalla.app/stats; echo
```

```json theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
{"users":0,"generated_at":"2026-10-30 05:29:41"}
{"users":0,"generated_at":"2026-10-30 05:29:41"}
```

The same `generated_at` in both shows that the second response came from Redis. A `500` with `read error on connection` in the runtime logs means the application can't reach Redis: see [Troubleshoot](#troubleshoot).

## Troubleshoot

| Symptom | Cause | Fix |
| - | - | - |
| Pages that use the cache return `500`, and the runtime logs show `production.ERROR: read error on connection to tcp://<host>:6379` | The application has no internal connection to the Redis database. | Add the internal connection on the application's **Networking** tab. It applies at once, without a deployment. |

## Deploy with the API

The same steps as [Create a Redis database](#create-a-redis-database), as calls to the [Sevalla API](https://docs.sevalla.com/developer-tools/api.md). `APP_ID`, `CLUSTER_ID` and `DB1_ID` are the application, its location and the **DB1** size from [Deploy with the API](https://docs.sevalla.com/guides/php/laravel/deploy.md#deploy-with-the-api) in the deploy guide, with `API` and `H` set as there. Push the `/stats` route first: the deployment builds the branch as it is.

**1. Look up the Redis version**

[List database versions](https://api-docs.sevalla.com/v3/resources/list-database-versions) (`listDatabaseVersionsV3`) gives the newest Redis version in the location. For Valkey, select `valkey` here and in the next step:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
REDIS_VERSION=$(curl -s "$API/resources/database-versions?cluster_id=$CLUSTER_ID" -H "$H" \
  | jq -r '.[] | select(.type == "redis") | .versions[-1]')
```

CLI: `sevalla resources db-versions --cluster $CLUSTER_ID --type redis`. MCP: `sevalla_api_read` with `listDatabaseVersionsV3`, or leave the version out of `create_database`, which takes the newest.

**2. Create the database**

[Create database](https://api-docs.sevalla.com/v3/databases/create-database) (`createDatabaseV3`). Redis has no user, and the API needs a `db_name` that it doesn't use for Redis. Poll [Get database](https://api-docs.sevalla.com/v3/databases/get-database) (`getDatabaseV3`) until it's `ready`; on `error`, delete the database and create it again:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
REDIS_PASSWORD=$(openssl rand -hex 16)
REDIS_ID=$(curl -s -X POST "$API/databases" -H "$H" -H "Content-Type: application/json" \
  -d "$(jq -n --arg cluster "$CLUSTER_ID" --arg size "$DB1_ID" --arg version "$REDIS_VERSION" --arg password "$REDIS_PASSWORD" \
    '{display_name: "storefront-redis", type: "redis", version: $version, cluster_id: $cluster,
      resource_type_id: $size, db_name: "storefront", db_password: $password}')" \
  | jq -r .id)
for i in $(seq 1 60); do
  DB_STATUS=$(curl -s "$API/databases/$REDIS_ID" -H "$H" | jq -r '.status // empty')
  case $DB_STATUS in ready|error) break ;; esac
  sleep 10
done
echo "$DB_STATUS"
```

CLI: `sevalla databases create --name storefront-redis --type redis --db-version $REDIS_VERSION --cluster $CLUSTER_ID --resource-type $DB1_ID --db-name storefront --db-password $REDIS_PASSWORD`. MCP: `create_database` generates a password that it never returns. Pass `connect_to_application_id` and `env_var_name: "REDIS_URL"`: the tool connects the database and stores `redis://:<password>@<host>:<port>` in `REDIS_URL`, which Laravel reads in place of the other `REDIS_*` variables. Then skip the next step, and add only `CACHE_STORE` in the one after.

**3. Connect it to the application**

[Create internal connection](https://api-docs.sevalla.com/v3/applications/create-internal-connection) (`createApplicationInternalConnectionV3`). It prints the connection's ID; if it prints nothing or `null`, run it again. The API doesn't add the connection's variables, so the next step does:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
curl -s -X POST "$API/applications/$APP_ID/internal-connections" -H "$H" -H "Content-Type: application/json" \
  -d "$(jq -n --arg db "$REDIS_ID" '{target_id: $db, target_type: "database"}')" | jq -r .id
```

CLI: `sevalla apps internal-connections create $APP_ID --target-id $REDIS_ID --target-type database`. MCP: `sevalla_api_create` with `createApplicationInternalConnectionV3` and the same body.

**4. Add the environment variables**

[Create environment variable](https://api-docs.sevalla.com/v3/applications/create-environment-variable) (`createEnvironmentVariableV3`), once per variable: the connection's variables in the dashboard's names and format, then `CACHE_STORE`, all for runtime only. For a Valkey database, the dashboard writes `REDIS_URL` with `valkey://`:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
REDIS_HOST=$(curl -s "$API/databases/$REDIS_ID" -H "$H" | jq -r .internal_hostname)
REDIS_PORT=$(curl -s "$API/databases/$REDIS_ID" -H "$H" | jq -r .internal_port)
for pair in "REDIS_HOST=$REDIS_HOST" "REDIS_PORT=$REDIS_PORT" "REDIS_PASSWORD=$REDIS_PASSWORD" \
  "REDIS_URL=redis://:$REDIS_PASSWORD@$REDIS_HOST:$REDIS_PORT/0" "CACHE_STORE=redis"; do
  curl -s -X POST "$API/applications/$APP_ID/env-vars" -H "$H" -H "Content-Type: application/json" \
    -d "$(jq -n --arg key "${pair%%=*}" --arg value "${pair#*=}" '{key: $key, value: $value, is_runtime: true, is_buildtime: false}')" \
    | jq -r .key
done
```

CLI: `sevalla apps env-vars create $APP_ID --key CACHE_STORE --value redis`, once per variable, for build and runtime. MCP: `set_environment_variables` with `runtime: true` and `buildtime: false`.

**5. Deploy**

[Trigger deployment](https://api-docs.sevalla.com/v3/applications/trigger-deployment) (`triggerDeploymentV3`):

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
DEPLOYMENT_ID=$(curl -s -X POST "$API/applications/$APP_ID/deployments" -H "$H" -H "Content-Type: application/json" \
  -d '{}' | jq -r .id)
```

CLI: `sevalla apps deployments trigger $APP_ID`. MCP: `trigger_deployment`.

**6. Wait for the deployment**

Poll [Get deployment](https://api-docs.sevalla.com/v3/applications/get-deployment) (`getDeploymentV3`) until the status is final: `success`, `failed`, `cancelled` or `skipped`. The loop keeps polling through a failed request and stops after 30 minutes:

```bash theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
for i in $(seq 1 120); do
  STATUS=$(curl -s "$API/applications/$APP_ID/deployments/$DEPLOYMENT_ID" -H "$H" | jq -r '.status // empty')
  case $STATUS in success|failed|cancelled|skipped) break ;; esac
  sleep 15
done
echo "$STATUS"
```

CLI: `sevalla apps deployments get $DEPLOYMENT_ID --app-id $APP_ID`. MCP: `get_deployment`.

Then run the checks in [Check from a terminal](#check-from-a-terminal).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.