This article addresses some of the common challenges you’re likely to encounter when configuring and deploying a Laravel application on Google App Engine.
Google App Engine (GAE) is a fully managed platform for running web and mobile apps in the cloud. Your GAE app runs in Docker containers and benefits from features such as automatic scaling, load balancing, health-checking and monitoring. In short, GAE lets you offload many of your infrastructure concerns.
You don’t need to be familiar with Docker in order to use GAE. Google Cloud will build the images for you and orchestrate the necessary infrastructure according to a configuration file you provide. And if you ever decide that the default setup is lacking you are free to customize the runtime by supplying your own Docker image.
To know whether App Engine is suitable for your project check out the official website.
You should also note that GAE apps run in one of two “environments”: “standard” and “flexible”. This article assumes you’ve chosen the flexible environment which is, as the name implies, more flexible. It’s also a more expensive option. Please refer to the official documentation to learn about pricing, capabilities and limitations.
This article is not a replacement for the official App Engine documentation. If you plan on deploying a production app to Google App Engine you should familiarize yourself with the platform by reading the documentation.
Make sure you’ve done the following before you continue:
- Create a Google Cloud account.
- Create a Google App Engine project.
- Install the Cloud SDK on your local
machine. This gives you a command-line interface (
gcloud) to the Google Cloud Platform which is used to deploy and manage your application.
- Create a Laravel project locally.
Your GAE application is configured using a file named
app.yaml. This file
serves as the basis for every deployment and should be placed in your project’s root
directory. While there are many available configuration options
which you should eventually explore, the
app.yaml for a Laravel project can be
as simple as this:
env_variables section should contain all your environment variables,
i.e. the variables you usually put in the
.env file. Remember to respect the
YAML format when copying variables from
app.yaml: The syntax is
GAE will make sure all your Composer dependencies are installed (by running
composer install) before your application is uploaded to the cloud. However,
there are certain custom commands we want to execute immediately after our
dependencies are installed. We can make use of Composer’s
post-install-cmd event which is triggered after
composer install by placing
the following in our
The first command makes our cache directory writable. The second creates a cache file for faster configuration loading, while the third creates a route cache for faster route registration. More information about these commands can be found in the Laravel documentation or in this article.
The commands will run each time a new release is deployed.
Deploying your app is as easy as running the following command:
gcloud app deploy
Behind the scenes GAE will build a Docker image containing your code, install
the dependencies (which will trigger the
post-install-cmnd commands) and
upload the image to the Google App Engine servers. When the container (your app)
starts to run, all traffic is directed to this specific release.
Laravel offers a unified API across a variety of different queue backends that allows you to defer time consuming tasks such as sending emails, talking to external APIs and generating reports.
While there are specialized tools that are better suited for queueing jobs (e.g. Beanstalk or Redis), the simplest way to get started is by storing jobs in the database you’re already using – MySQL or PostgreSQL. Using the database driver saves us from having to spin up another virtual machine or launching another App Engine service.
The Laravel documentation will walk you
through the process of creating, dispatching and deferring jobs. In the end we
want to run our worker by executing the
artisan queue:work command.
Luckily Google App Engines lets us provide a Supervisor configuration for our
app. Supervisor is a process monitor that will make sure our
process never stops running.
Create the file
Then (re)deploy your app with
gcloud app deploy and Google App Engine will
make sure Supervisor monitors your worker.
(Note: Running multiple service instances will lead to multiple workers running simultaneously. If your app generates a lot of traffick you should consider using another, more specialized, queue driver.)
Google Stackdriver is a monitoring service for cloud-powered applications. You can use it to log and collect metrics from any part of your infrastructure including Google App Engine. By integrating your Laravel app with Stackdriver you’ll be able to view your application logs in the Cloud Console and receive notifications (email and push!) when errors are detected.
In order to log messages to Google Stackdriver you need to install the log client for Google Cloud:
composer require google/cloud-logging google/cloud-error-reporting
true in the
Laravel is able to write logs to different channels. While several channels
are provided out of the box we need to create our own custom channel that sends
log entries to Stackdriver. Configure a new channel by adding a new entry to
channels array in
'channels' => [
The name of our channel is “stackdriver” and the
via option points to a
factory class which will be invoked to create a Monolog instance. Monolog is the
underlying logging library used by Laravel, and we need to instantiate it in a
specific way in order to make it write to Stackdriver.
Create the custom Monolog factory in a new file named
CreateStackdriverLogger.php and place it in
To make sure our new channel is used in production add the
LOG_CHANNEL key to the
env_variables section of
Laravel will use this environment variable to determine which log channel to
use. If you haven’t modified it,
config/logging.php should should have the
following line by default:
'default' => env('LOG_CHANNEL', 'stack'),
That takes care of logging.
Exceptions should also be reported to Stackdriver. In
add a conditional to check whether the app is running on GAE. If that’s the case
we want the Google Cloud library handle exceptions:
// Import this at the top of the file
The standard Laravel exception handler lets you exclude certain exceptions from
being reported by adding them to the
$dontReport array. This is useful if you
don’t want to litter your logs with 404 errors and other uninteresting events.
As you can see in the code snippet above, we retain this behavior by checking
shouldReport() before passing the exception to Google’s exception handler.
Stackdriver provides many features and configuration options that can help you monitor your app’s health. You should check out the Cloud Console to see if there’s any particular feature or option you want to explore further.
GAE doesn’t populate the variable
$_SERVER['REMOTE_ADDR] with the IP address
of the client that sent the request. This means that the convenient helper
$request->getClientIp() won’t return anything. However, you can find
the client’s IP in the
X-Forwarded-For header which App Engine populates with
a comma-delimited list of proxy IP addresses through which the request has been
routed. The documentation states:
The first IP in this list is generally the IP of the client that created the request. The subsequent IPs provide information about proxy servers that also handled the request before it reached the application server.
With this in mind we can create a middleware that populates
the client’s IP address by extracting the first item in
Remember to add this middleware in the
middleware array in
app/Http/Kernel.php for it to run on every request.
You can now call
$request->getClientIp() to retrieve your visitor’s IP address.
App Engine can automatically renew SSL/TLS certificates for you. Certificate management is configured in the Cloud Console.
There’s no dedicated configuration option in App Engine to redirect HTTP traffic to HTTPS. This must be done at the application level, either using custom NGINX configuration files or a middleware in your Laravel application.
Here’s an example middleware:
You should also consider HTTPS Strict Transport Security (HSTS). This is a web policy that helps to protect against downgrade attacks by telling clients that the website should only be accessed through secure HTTPS connections. Websites communicate this policy by sending a HSTS response header:
The header instructs conforming clients that subsequent communication with the website should only happen over HTTPS. Also, the header should only be sent over secure connections. You should read up on HSTS if you’re not familiar with the policy.
Here’s a middleware that sets the relevant header on every (secure) response: