Deploy Laravel and Memcache on Render.com: The Complete Guide
Do you want to deploy a Laravel 10 application on Render and boost its performance with Memcache? In this tutorial, you’ll create a simple Laravel task list app, deploy it to Render, and use Memcache to speed it up.
Memcache is an in-memory data storage system, a tool that significantly enhances the efficiency and scalability of web applications. Consider integrating Memcache if your pages exhibit sluggish load times or your application faces scaling challenges. Regardless of your app’s size, Memcache has the potential to quicken page loads and help future-proof your app.
Render.com is a cloud service platform that provides developers with tools and services to host and deploy a wide range of web applications, static sites, and background workers.
Laravel is a robust, open-source PHP framework that provides a simple, elegant syntax and tools to build full-featured web applications.
Outline
- Initialize a Laravel project
- Create a Laravel task list app
- Configure your app for Render
- Deploy Laravel to Render
- Set up caching in Laravel
- Implement caching strategies in Laravel
Prerequisites
- Familiarity with PHP and, ideally, Laravel.
- A Render account.
- A GitHub account and Git installed on your local machine. To deploy to Render, you must push code to a remote code repository. Render supports GitHub and GitLab.
- PHP, Composer, and Git installed on your computer.
Initialize a Laravel project
To start, create a new Laravel project. In your terminal, run the following command:
composer create-project laravel/laravel laravel_memcache
That command will use the latest version of Laravel, which at the time of writing v10.2.5
.
# output
Creating a "laravel/laravel" project at "./laravel_memcache"
Info from https://repo.packagist.org:
Installing laravel/laravel (v10.2.5)
- Downloading laravel/laravel (v10.2.5)
- Installing laravel/laravel (v10.2.5): Extracting archive
...
Next, change to the directory just created:
cd laravel_memcache
Then, install Laravel’s NPM dependencies:
npm install
And start Laravel’s development server using the Artisan CLI serve command:
php artisan serve
Once you have started the development server, you can view the Laravel skeleton app in your web browser at http://localhost:8000
.
Create a Laravel task list app
In this step, you’ll add a task list to the app, enabling users to view, add, and delete tasks. To accomplish this, you need to:
- Set up a local database.
- Create the
Task
model. - Create the view and controller logic to add, view, and delete tasks.
Set up a local database (Optional)
To test your app locally, we recommend using an SQLite database. Install SQLite on your computer if necessary.
Add the database by creating the file database/database.sqlite
.
To use this connection locally, update DB_CONNECTION
and remove DB_DATABASE
in your app’s .env
file:
# .env
# ...
DB_CONNECTION=sqlite
# ...
# DB_DATABASE=laravel
# ...
You remove the DB_DATABASE
variable because the default SQLite config looks for it before defaulting to the desired database.sqlite
database. If you look at the SQLite configuration in config/database.php
, you’ll find 'database' => env('DB_DATABASE', database_path('database.sqlite'))
.
Create the Task model
To create and store tasks, we need to do three things:
First, create a migration that will create the tasks
table:
php artisan make:migration create_tasks_table --create=tasks
That command creates a migration named create_tasks_table
.
Tasks should have names, so add a name
field to the tasks
table schema in the newly created database/migrations/<date>_create_tasks_table.php
file. Note the date in your filename will be generated depending on when you run the command. It will look something like 2023_07_12_081805_create_tasks_table.php
:
// database/migrations/<date>_create_tasks_table.php
// ...
Schema::create('tasks', function (Blueprint $table) {
$table->id();
$table->string('name'); // Add this line
$table->timestamps();
;
})// ...
Second, create a Task
model to easily interact with the tasks
table from our code:
php artisan make:model Task
This command creates an empty Task
model in app/Task.php
. Laravel automatically infers the Task
model fields from the migration you created in the previous step.
Third, run the migrations:
php artisan migrate
Add a view for the task list
To see the tasks stored in the database, create a view that displays all tasks.
Create the file resources/views/layouts/app.blade.php
, open it in your editor, and add the following:
<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MemCachier Laravel Tutorial</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
@vite('resources/css/app.css')
</head>
<body>
@yield('content')
@vite('resources/js/app.js')
</body>
</html>
Note we’ll include the app.css
and app.js
files generated with a new Laravel project, though we won’t add any CSS or JavaScript in this guide.
Now, create the task list view as a child view of the app.blade.php
layout:
Create the file resources/views/tasks.blade.php
, open it in your editor, and add the following:
<!-- resources/views/tasks.blade.php -->
@extends('layouts.app')
@section('content')
<!-- TODO: New Task Card -->
<!-- Current Tasks -->
@if (count($tasks) > 0)
<h2>Current Tasks</h2>
<ul>
@foreach ($tasks as $task)
<li>
<!-- Task Name -->
<div>{{ $task->name }}</div>
<!-- TODO Delete Button -->
</li>
@endforeach
</ul>
@endif
@endsection
Ignore the TODOs
for now. You’ll fill them out later.
To access this view, connect it to the root route, GET /
, in routes/web.php
:
// routes/web.php
use Illuminate\Support\Facades\Route;
use App\Models\Task;
// ...
// Show Tasks
Route::get('/', function () {
$tasks = Task::orderBy('created_at', 'asc')->get();
return view('tasks', [
'tasks' => $tasks
;
]); })
With these changes, you import the Task
model. Then, in the root route, you get tasks ordered by their creation date and pass that task data to the view.
Enable task creation
Next, add the ability to create tasks. Open up resources/views/tasks.blade.php
and replace the <!-- TODO: New Task Card -->
comment with the following:
<!-- resources/views/tasks.blade.php -->
<!-- ... -->
<!-- New Task Card -->
<div>
<h2>New Task</h2>
<!-- New Task Form -->
<form action="{{ url('tasks') }}" method="POST">
{{ csrf_field() }}
<label>
Task Name: <input type="text" name="name" placeholder="e.g. Wash the dishes" required>
</label>
<button>Add Task</button>
</form>
</div>
<!-- Current Tasks -->
<!-- ... -->
For security, validating user input, such as when a task is added, is essential. However, we’ll forego validating data to concentrate on the primary objectives of this tutorial: deploying Laravel on Render and caching with Memcache.
Let’s add these new views to routes/web.php
:
// routes/web.php
// ...
use Illuminate\Support\Facades\Route;
use App\Models\Task;
use Illuminate\Http\Request;
// ...
// Show Tasks
// ...
// Add New Task
Route::post('/tasks', function (Request $request) {
// Create task
$task = new Task;
$task->name = $request->name;
$task->save();
return redirect('/');
; })
Enable task deletion
To complete your task list functionality, you’ll add the ability to delete a task. Add a Delete button to each item in the task list to delete a task. In resources/views/tasks.blade.php
, replace the <!-- TODO Delete Button -->
comment with the following changes:
<!-- resources/views/tasks.blade.php -->
<!-- ... -->
<!-- Delete Button -->
<form action="{{ url('tasks/'.$task->id) }}" method="POST">
{{ csrf_field() }}'DELETE') }}
{{ method_field(
<button>Delete</button>
</form>
<!-- ... -->
Then, wire this functionality to the appropriate route in routes/web.php
:
// routes/web.php
// ...
// Show Tasks & Add New Task
// ...
// Delete Task
Route::delete('/tasks/{task}', function (Task $task) {
$task->delete();
return redirect('/');
; })
If you have set up your app locally, it’s time to see how it works in the browser.
Now that you include assets with the @vite
directive, you must run the Vite development server for the Laravel development server to work. Open a new terminal tab or window and start the Vite development server to build and live reload your assets:
npm run dev
You can now start a web server if you have set up the app locally. In your terminal, run the following:
php artisan serve
If you don’t run npm run dev
, you’ll get an error Vite manifest not found at: <your_local_path>/public/build/manifest.json
. If you get the same error in production, see the step Create a Dockerfile
to run Nginx and PHP.
Finally, view the app at localhost:8000
. You should now be able to add, view, and delete tasks.
Configure your app for Render
To prepare your Laravel app to be deployed to Render, you’ll do the following:
- Create a
Dockerfile
to run Nginx and PHP - Configure Nginx
- Create a deploy script
- Create a
.dockerignore
file - Force HTTPS on all assets served by Laravel
- Define your Render infrastructure as code
- Create a remote code repository for your app
Create a Dockerfile
to run Nginx and PHP
At the time of writing, Render Web Services have native support for Node.js, Python, Ruby, Elixir, Go, and Rust, but not PHP. Fortunately, Render supports Docker, allowing you to create a Dockerfile
to deploy anything you need.
You’ll create a Dockerfile
to provision a Render web service that runs Nginx with PHP, enabling you to run a Laravel app.
Create a file named Dockerfile
, open it up in your code editor, and add the following:
# Dockerfile
# Use base image for container
FROM richarvey/nginx-php-fpm:3.1.6
# Copy all application code into your Docker container
COPY . .
RUN apk update
# Install the `npm` package
RUN apk add --no-cache npm
# Install NPM dependencies
RUN npm install
# Build Vite assets
RUN npm run build
CMD ["/start.sh"]
Later, once your app is deployed, if you get the error Vite manifest not found at: /var/www/html/public/build/manifest.json
, it’s most likely because npm run build
is not being called during the deploy process, and so your Vite assets and Vite manifest.json
file are not being built. Refer to this step and ensure npm install
and npm run build
are being called.
Similarly, if you get the error sh: vite: not found
, it’s likely npm install
was not called, meaning vite
was not installed.
Note you use the apk
package manager as the richarvey/nginx-php-fpm
base image runs on Alpine Linux.
Next, you’ll configure Nginx for your Docker container.
Configure Nginx
To add Nginx configuration to your Docker container, create the file conf/nginx/nginx-site.conf
.
By default, the richarvey/nginx-php-fpm
Docker image looks for configuration files in a conf
folder. See the image Nginx config documentation.
# nginx-site.conf
server {
# Render provisions and terminates SSL
listen 80;
# Make site accessible from http://localhost/
server_name _;
root /var/www/html/public;
index index.html index.htm index.php;
# Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html
sendfile off;
# Add stdout logging
error_log /dev/stdout info;
access_log /dev/stdout;
# Block access to sensitive information about git
location /.git {
deny all;
return 403;
}
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~* \.(jpg|jpeg|gif|png|css|js|ico|webp|tiff|ttf|svg)$ {
expires 5d;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
include fastcgi_params;
}
# deny access to . files
location ~ /\. {
log_not_found off;
deny all;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
This Nginx configuration is taken from Render’s Laravel quickstart repo. The Nginx configuration is still accurate. However, their repo uses Laravel 5 and will not work with Laravel 10. Not to worry, though. We have you covered in this tutorial.
Create a deploy script
Create a file scripts/00-laravel-deploy.sh
and add the following to it:
#!/usr/bin/env bash
echo "Running composer install..."
composer install --no-dev --working-dir=/var/www/html
echo "Running migrations..."
php artisan migrate --force
For the command php artisan migrate --force
, the --force
option will run the migrations without prompting for confirmation.
You could add any other commands you want to this file. For example, php artisan config:cache
or php artisan route:cache
.
In the upcoming step, Define your Render infrastructure as code, you will set the flag RUN_SCRIPTS
to 1
to run this script once your container is ready.
Create a .dockerignore
file
Next, create a .dockerignore
file to avoid copying unnecessary files to your Docker container. Ignoring unnecessary files can make your Docker build faster. Open the file and add the following:
# .dockerignore
.DS_Store
.dockerignore
.editorconfig
.env
.env.backup
.env.example
.env.production
.git
.gitattributes
/.fleet
/.idea
/.phpunit.cache
/.vscode
Homestead.json
Homestead.yaml
Dockerfile
README.md
/auth.json
/node_modules
/npm-debug.log
/phpunit.result.cache
/public/build
/public/hot
/public/storage
/phpunit.xml
storage/app/*
storage/framework/cache/*
storage/framework/sessions/*
storage/framework/views/*
storage/logs/*
tests
/vendor
yarn-error.log
Force HTTPS on all assets served by Laravel
In this step, you’ll force HTTPS on all assets served by Laravel to avoid mixed content warnings in the browser.
Open up app/Providers/AppServiceProvider.php
in your code editor. Import UrlGenerator
and update the boot
method as follows:
# app/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Routing\UrlGenerator;
class AppServiceProvider extends ServiceProvider
{// ...
/**
* Bootstrap any application services.
*/
public function boot(UrlGenerator $url): void
{if (env('APP_ENV') == 'production') {
$url->forceScheme('https');
}
} }
Next, you’ll write a Render Blueprint spec to define your Render infrastructure as code.
Define your Render infrastructure as code
Create a render.yaml
Blueprint spec file in your project root directory. You’ll define a Docker Web Service and PostgreSQL Database in the spec.
# render.yaml
databases:
- name: laravel_memcache_db
plan: free
databaseName: laravel_memcache_db
user: laravel_memcache_db
services:
- type: web
plan: free
name: laravel_memcache
runtime: docker
envVars:
# Database
- key: DATABASE_URL
fromDatabase:
name: laravel_memcache_db
property: connectionString
- key: DB_CONNECTION
value: pgsql
# Laravel
- key: APP_KEY
sync: false
- key: APP_ENV
value: production
- key: APP_DEBUG
value: false
- key: LOG_CHANNEL
value: stderr
# Image-specific vars (richarvey/nginx-php-fpm)
- key: WEBROOT
value: /var/www/html/public
- key: RUN_SCRIPTS
value: 1
- key: REAL_IP_HEADER
value: 1
- key: SKIP_COMPOSER
value: 1
Note you’re specifying Render’s Free plan for both the database and web service, which is sufficient for this tutorial. The default plan is Starter.
The web service environment variable DATABASE_URL
imports a Postgres connection string from the laravel_memcache_db
database you defined.
The APP_KEY
environment variable has the property sync: false
, making it a Render placeholder environment variable. Placeholder environment variables are those which you want to exclude from your Blueprint spec and are not generated, such as external secrets. You will later be prompted to enter a generated APP_KEY
value on the Render dashboard before your deployment is finalized.
Setting LOG_CHANNEL
to stderr
ensures logs will appear in the Logs tab of your Render Web Service dashboard.
With the flag RUN_SCRIPTS
to 1
, the scripts/00-laravel-deploy.sh
script you created will run once your container is ready. Script files in the /scripts
folder will be run in order of prefixed number. A file beginning with 00-
would run first, 01-
would run second, and so on. For more info, see the repository layout documentation for the richarvey/nginx-php-fpm image.
For explanations of the other richarvey/nginx-php-fpm
image-specific environment variables, see the Available Configuration Parameters documentation.
See the Render Blueprint spec documentation for an explanation of all properties in your render.yaml
file.
Create a remote code repository for your app
To deploy to Render, you need a remote code repository. In this step, you’ll commit your code to your app’s git repository and push it to GitHub.
Apps generated with the create-project
CLI command come with a .gitignore
file. So you can initialize a Git repo:
git init
Stage your code:
git add .
And commit:
git commit -m 'Initial commit: Create task list app'
Now your code is committed, you’ll push it to GitHub.
In your browser, log in to GitHub and create an empty repository called laravel_memcache. The repo can be public or private.
Back in the terminal, add your GitHub repo as a remote origin, replacing your_username
with your actual GitHub username:
git remote add origin https://github.com/<your_username>/laravel_memcache.git
That command tells Git where to push your code to.
Rename the default branch:
git branch -M main
And push your code to GitHub:
git push -u origin main
Your app’s code is now on GitHub, ready to be deployed to Render.
Deploy Laravel to Render
Now that your code is on GitHub, deploying to Render is as simple as creating a new Blueprint Instance and connecting your GitHub repository.
Log in to Render and go to your Dashboard. Click New +, then click Blueprint.
Connect your GitHub account if you still need to do so. Then, find your repo in the Connect a repository section of the Create a new Blueprint Instance page and click Connect.
After clicking Connect you’ll be taken to a settings page. Choose a Blueprint Name. I’ll stick with laravel_memcache. Note the region you’re creating your app in. You’ll need it later.
Next, generate your own APP_KEY
. In your terminal window, run the following command:
php artisan key:generate --show
Copy the generated value and set it as the APP_KEY
environment variable value.
Finally, click Apply to begin deploying.
If the web service deployment fails and you receive a notice An error has occurred
, click on the name of the web service to display its Events tab. You’ll see an event for the failed deployment. Click on deploy logs in the event to investigate.
When the deployment is live, go to your Render Dashboard, click on your Web Service, and finally click on its URL under the Web Service name to open it in your browser. The task list app should work as it does locally.
If you see a 502 Bad Gateway
error in the browser, return to the step Configure Nginx and make sure the path to your Nginx configuration file is correct, conf/nginx/nginx-site.conf
.
Next, you’ll implement caching.
Set up caching in Laravel
Setting up caching with Memcached in Laravel involves the following steps:
- Create a Memcached-compatible cache
- Install the
memcached
PHP extension - Configure Laravel to use Memcached
Create a Memcached-compatible cache
In this tutorial, you’ll create a free Memcached-compatible cache with MemCachier. For another option to use Memcached on Render, read our blog post to learn how to run Memcached as a Render Private Service.
MemCachier is a fully managed caching service that simplifies setting up and using Memcached in your web applications. Built for seamless integration with cloud platforms like Render, MemCachier offers developers a streamlined and hassle-free way to implement high-performance caching in their applications without worrying about the complexities of managing a cluster of Memcached servers.
To begin, create a new MemCachier cache. Choose Render as the provider. Choose the same region as your Render Web Service. Choose the Free plan. Finally, click CREATE CACHE.

After creating your cache, you’ll find its configuration settings (Username, Password, Servers) on the CACHES dashboard.

On your Render dashboard, select your Web Service, then select the Environment tab. Then, add your MemCachier Servers, Username, and Password config values, naming the environment variables MEMCACHIER_SERVERS
, MEMCACHIER_USERNAME
, and MEMCACHIER_PASSWORD
, respectively.

Click Save Changes. Next, you’ll configure Laravel to use your cache.
Install the memcached
PHP extension
Update your Dockerfile
to install the memcached
PHP extension and its required packages:
# Dockerfile
# ...
# Build Vite assets
RUN npm run build
# Install required packages
RUN apk add --no-cache autoconf g++ make libmemcached-dev
# Install the `memcached` PHP extension with SASL auth enabled
RUN pecl install --configureoptions 'enable-memcached-sasl="yes"' memcached
# Enable the `memcached` PHP extension
RUN docker-php-ext-enable memcached
CMD ["/start.sh"]
The libmemcached-dev
package is installed because it depends on the memcached
PHP extension. The packages autoconf
, g++
, and make
are all required for the pecl install ...
command to work. If they are missing, you’ll get the error messages Cannot find autoconf
, no acceptable C compiler found in $PATH
a, and sh: make: not found
respectively.
The memcached
PHP extension is installed with SASL support enabled because MemCachier caches require SASL (username and password) authentication.
Configure Laravel to use Memcached
Configure the cache by updating the memcached
settings in config/cache.php
. Set the CACHE_DRIVER
to memcached
and replace the memcached
configuration in the stores
array with that listed below:
// config/cache.php
//..
'default' => env('CACHE_DRIVER', 'memcached'),
// ...
'stores' => [
// ...
'memcached' => [
'driver' => 'memcached',
'persistent_id' => 'memcached_pool_id',
'sasl' => [
'MEMCACHIER_USERNAME'),
env('MEMCACHIER_PASSWORD'),
env(,
]'options' => [
// some nicer default options
// - nicer TCP options
Memcached::OPT_TCP_NODELAY => TRUE,
Memcached::OPT_NO_BLOCK => FALSE,
// - timeouts
Memcached::OPT_CONNECT_TIMEOUT => 2000, // ms
Memcached::OPT_POLL_TIMEOUT => 2000, // ms
Memcached::OPT_RECV_TIMEOUT => 750 * 1000, // us
Memcached::OPT_SEND_TIMEOUT => 750 * 1000, // us
// - better failover
Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_CONSISTENT,
Memcached::OPT_LIBKETAMA_COMPATIBLE => TRUE,
Memcached::OPT_RETRY_TIMEOUT => 2,
Memcached::OPT_SERVER_FAILURE_LIMIT => 1,
Memcached::OPT_AUTO_EJECT_HOSTS => TRUE,
,
]'servers' => array_map(function($s) {
$parts = explode(":", $s);
return [
'host' => $parts[0],
'port' => $parts[1],
'weight' => 100,
;
], explode(",", env('MEMCACHIER_SERVERS', 'localhost:11211')))
},
]
// ...
,
]
//
These changes configure the Laravel caching engine with MemCachier, allowing you to use your Memcache in a few different ways:
- Directly access the cache via
get
,set
,delete
, and so on - Cache results of functions with the
rememberForever
function - Use Memcache for session storage
- Cache entire responses
Implement caching strategies in Laravel
In this section, you’ll implement several caching techniques in Laravel:
- Caching resource-intensive database queries
- Using Memcache for session storage
- Caching entire responses
Cache expensive database queries
Memcache is often used to cache the results of expensive database queries. Of course, our simple task list does not have any resource-intensive queries, but let’s assume that fetching all the tasks from the database is a slow operation for this tutorial.
The rememberForever
function makes it easy to add caching to Laravel.
You provide two arguments for it:
- A cache key
- A function that queries your database and returns results
The rememberForever
function looks up the key in your cache. If the key is present, its corresponding value is returned. Otherwise, the database function you provided is called. Whatever that function returns is stored in the cache with the corresponding key for future lookups.
The first time you call rememberForever
, the resource-intensive database function is called, but every successive call to rememberForever
obtains the value from the cache.
Use the rememberForever
function to add caching to the task view
controller in routes/web.php
:
// routes/web.php
// ...
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
// ...
// Show Tasks
Route::get('/', function () {
$tasks = Cache::rememberForever('all_tasks', function () {
return Task::orderBy('created_at', 'asc')->get();
;
})
return view('tasks', [
'tasks' => $tasks
;
]); })
As you might have noticed, we now have a problem if we add or remove a task.
Because rememberForever
fetches the task list from the cache, any changes
to the database won’t be reflected in the task list. For this reason,
whenever we change the tasks in the database, we need to invalidate the
cache:
// routes/web.php
// Add New Task
Route::post('/tasks', function (Request $request) {
// ...
$task->save();
Cache::forget('all_tasks');
return redirect('/');
;
})
// Delete Task
Route::delete('/tasks/{task}', function (Task $task) {
$task->delete();
Cache::forget('all_tasks');
return redirect('/');
; })
Redeploy the app to Render with:
git add .
git commit -m 'Add expensive database query caching'
git push
View Memcache statistics
To help demystify Memcache caching operations, it’s invaluable to be able to visualize what’s going on under the hood.
Though very cumbersome, one way to do that is to telnet
into a Memcached server and run the stats command to see changes as operations are performed on your cache.
With MemCachier, however, you get an analytics dashboard that displays your cache’s statistics to monitor performance and troubleshoot issues quickly and easily.
To open your MemCachier analytics dashboard, log in to your MemCachier account, click Caches, then click the Analytics button for your cache.

You can see that the first time you access the page, the get misses
increases
by one. This is because the first time rememberForever
is called, the task
list is not in the cache. The Set Cmds
also increases because the task list
is saved to the cache. If you refresh the page, the misses stay the same,
but the get hits
increases because the task list is served from the cache.
When you add a new task or delete a task, your misses will increase again because the cache was invalidated.
Using Memcache for session storage
Memcache works well for storing information for short-lived sessions that time out. However, because Memcache is a cache and therefore not persistent, long-lived sessions are better suited to permanent storage options, such as your database.
Changing the session store from file
(default) to memcached
is done by setting the SESSION_DRIVER
config variable. To do so, go to your Web Service Environment tab, and add the variable SESSION_DRIVER
with the value memcached
.
Now, the next time you load your app, get misses
will increase by an additional one, and your session will be cached. On subsequent page loads, get hits
will increase by an additional one, giving you two get hits
in total: one for the cached session and one for the cached tasks
query.
Laravel session config can be found in config/session.php
. Notice we have just set the driver
option by setting that environment variable. You can read more in the Laravel HTTP Session documentation.
Caching entire responses
In Laravel, it’s easy to cache the entire responses using
the laravel-responsecache package. Response caching in Laravel
is similar to view caching in Ruby on Rails. By default, laravel-responsecache
will cache all successful GET
requests that return text-based content (such as HTML and JSON) for one week.
Even though your create task form includes a CSRF token, laravel-responsecache
includes Replacers for replacing dynamic content in a cached response. By default, a CSRF replacer is included and works without additional configuration. Custom Replacers can also be written.
To use laravel-responsecache
, first, install the package:
composer require spatie/laravel-responsecache
Next, install the provided middlewares \Spatie\ResponseCache\Middlewares\CacheResponse::class
and \Spatie\ResponseCache\Middlewares\DoNotCacheResponse
in the http kernel:
// app/Http/Kernel.php
// ...
protected $middlewareGroups = [
'web' => [
// ...
Spatie\ResponseCache\Middlewares\CacheResponse::class,
\,
]
// ...
protected $middlewareAliases = [
...'doNotCacheResponse' => \Spatie\ResponseCache\Middlewares\DoNotCacheResponse::class,
; ]
And finally, set the RESPONSE_CACHE_DRIVER
web service environment variable to memcached
. Open up your Render Web Service dashboard, go to the Environment tab, and add the variable RESPONSE_CACHE_DRIVER
with the value memcached
.
Commit and deploy your code:
git add .
git commit -m 'Add response caching'
git push
Notice that tasks can no longer be added or deleted. This is because the cached response is not invalidated when its contents change. The response must be invalidated as when you cached the tasks
query.
To invalidate or clear the cached response, make the following changes to routes/web.php
:
// routes/web.php
...use Illuminate\Support\Facades\Cache;
use Spatie\ResponseCache\Facades\ResponseCache;
...
// Add New Task
Route::post('/tasks', function (Request $request) {
...
Cache::forget('all_tasks');
ResponseCache::forget('/');
return redirect('/');
;
})
// Delete Task
Route::delete('/tasks/{task}', function (Task $task) {
...
Cache::forget('all_tasks');
ResponseCache::forget('/');
return redirect('/');
; })
With those changes, you clear the cache for a specific URI, /
. Several other options exist for invalidating the cache explained in the laravel-responsecache documentation.
Note, at this point, caching the tasks
query (which you did earlier to simulate caching an expensive computation) doesn’t add any additional caching benefit, as response caching takes care of caching that query. However, you could imagine a view using data from multiple queries. If one dataset changed, it would still be beneficial for the other unmodified queries to remain cached so that regenerating a response happens more quickly.
Profiling Memcache performance
So far, you’ve learned how to cache expensive database queries, sessions, and full-page responses. Knowing how our cache is performing as a whole and how individual parts work is helpful.
Imagine the corresponding keys of each caching strategy you implemented had prefixes: query_
, session_,
and response_
. Key prefixes would allow you to profile cache performance. MemCachier’s paid plans come with a powerful feature set called Introspection, allowing you to track the performance of individual prefixes, giving you deep insight into your cache. For example, you could monitor the hit rate, or hit ratio, of any prefix.

Clean up
Once you finish this tutorial and no longer need your app, you can delete your Web Service and Database from the Render dashboard. You could also delete the corresponding Blueprint from Blueprints.
You can also delete your MemCachier cache from the Caches dashboard if you no longer need it.