Deploying a Spiral application can be a complex task, as it requires various steps to be performed in order to ensure that the application is properly configured and ready for production use. In this guide, we will go over several strategies for deploying an application, including basic file transfer, using source control, build scripts, and Docker builds.
Deploying a Spiral application on a production server requires several key configurations to ensure proper operation and security.
Warning
Do not store.env
file in your repository as it may contain sensitive information such as database credentials, API keys, and other secrets.
Here is a step-by-step guide on how to configure a Spiral application on a production server:
Make sure that the DEBUG
variable is set to false
in your application's environment configuration. This will prevent
sensitive information from being displayed in case of an error.
DEBUG=false
Change the APP_ENV
variable in your application's environment configuration to production
. This will prevent
accidental actions that should only be run on a development server.
APP_ENV=production
Make sure that the TOKENIZER_CACHE_TARGETS
variable is set to true
in your application's environment configuration.
This will disable scanning and analysis of the application's source code, which can improve performance on boot.
TOKENIZER_CACHE_TARGETS=true
Note
Read more about tokenizer in Component — Static analysis section.
Set the VERBOSITY_LEVEL
variable to a basic
level to hide server errors from being displayed to the public.
VERBOSITY_LEVEL=basic
Set the desired MONOLOG_DEFAULT_CHANNEL
in the application's environment configuration. This will determine where the
application's logs will be stored. Also set the MONOLOG_DEFAULT_LEVEL
variable to error
in your application's
environment configuration. This will prevent debug and info errors from being logged, helping to keep your logs clean
and easy to read.
MONOLOG_DEFAULT_CHANNEL=roadrunner
MONOLOG_DEFAULT_LEVEL=error
If you are using the Cycle ORM, make sure that the CYCLE_SCHEMA_CACHE
and CYCLE_SCHEMA_WARMUP
variables are set to
true
in your application's environment.
The CYCLE_SCHEMA_CACHE
variable controls whether the ORM should cache the schema of the database tables. When set to
true
, the ORM will cache the schema, which can improve the performance of the application by reducing the number of
database queries needed to retrieve the schema information.
The CYCLE_SCHEMA_WARMUP
variable controls whether the ORM should warm up the schema cache when the application starts.
When set to true
, the ORM will pre-populate the schema cache with the schema information, which can further improve
the performance of the application by reducing the time it takes to retrieve the schema information.
CYCLE_SCHEMA_CACHE=true
CYCLE_SCHEMA_WARMUP=true
The following composer commands can be used to install vendor packages for a Spiral application:
composer install --optimize-autoloader --no-dev --no-scripts
This command installs the application's dependencies, generates the autoloader files for the application and optimizes them for performance.
--no-dev
option is used to prevent any development dependencies from being installed ad--no-scripts
option is used to prevent any post-install
scripts from being executed.If you want to use Spiral with RoadRunner and have Nginx server in front of it, you will need to configure Nginx to act as a reverse proxy.
Here is an example of how to set up Nginx as a reverse proxy.
server {
listen 80;
server_name _;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
This configuration instructs Nginx to listen on port 80
and forward all incoming requests to 127.0.0.1:8080
, where
RoadRunner is running.
You can place this configuration file in the /etc/nginx/sites-available/
directory and then create a symbolic link to
it in the /etc/nginx/sites-enabled/
directory.
Here is an example of a configuration that uses a RoadRunner HTTP server:
http:
address: 127.0.0.1:8080
Warning:
Avoid using address:0.0.0.0:8080
in the RoadRunner configuration as it would block direct access to the RoadRunner HTTP server, allowing access only through the Nginx reverse proxy.
To use Spiral with PHP-FPM, you'll need to follow these steps:
spiral/sapi-bridge
package, which provides a dispatcher that PHP-FPM will use to handle requests.Use the following command to install the package:
composer require spiral/sapi-bridge
Once the package is installed, you'll need to register the Spiral\Sapi\Bootloader\SapiBootloader
bootloader in your
application's list of bootloaders.
public function defineBootloaders(): array
{
return [
// ...
\Spiral\Sapi\Bootloader\SapiBootloader::class,
// ...
];
}
Read more about bootloaders in the Framework — Bootloaders section.
Here's an example of how you can set up Nginx to work with PHP-FPM:
server {
listen 80;
listen [::]:80;
server_name example.com;
root /srv/example.com/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index app.php;
charset utf-8;
location / {
try_files $uri $uri/ /app.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 /app.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
Using automation tools like Deployer can automate the deployment process and handle tasks such as database migrations, cache clearing and more. They also allow for faster deployment and rollback processes, and can be integrated with other tools like load balancers and monitoring systems. This can make the deployment process more efficient and less prone to errors, and makes it easier to keep track of which version of the code is currently on the production server, making it hard to rollback in case of errors.
Deployer is a popular deployment tool that can be used to automate the deployment process for a Spiral application. One of the advantages of using Deployer is that it can provide "zero downtime" deployment.
Zero downtime deployment is a technique that allows you to update your application without interrupting the service to your users. This is achieved by deploying the new version of the application alongside the old version, and then swapping them out once the new version is ready. This allows for a seamless transition, as users are not affected by any downtime during the deployment process.
Deployer provides the capability of Zero Downtime deployment by creating a new release of the application in a different directory and switching the symlink to the new release.
To install Deployer, run next command in your project dir:
To initialize deployer in your project run:
./vendor/bin/dep init
Deployer will ask you a few questions and after finishing you will have a deploy.php
or deploy.yaml
file. This is
your deployment recipe. It contains hosts, tasks and requires other recipes.
Here is an example of a deploy.php
file:
namespace Deployer;
require 'recipe/spiral.php';
set('repository', 'https://github.com/xxx/my-app');
add('shared_files', []);
add('shared_dirs', []);
add('writable_dirs', []);
host('example.org')
->set('remote_user', 'deployer')
->set('deploy_path', '/var/www/my-app');
after('deploy:failed', 'deploy:unlock');
desc('Deploys your project');
task('deploy', [
'deploy:prepare',
'deploy:environment',
'deploy:vendors',
'spiral:encrypt-key',
'spiral:configure',
'deploy:download-rr',
'deploy:publish',
'deploy:restart-rr'
]);
To connect to the remote host we need to specify an identity key or private key. We can add our identity key directly
into the host definition, but it's better to put it in the ~/.ssh/config
file:
Host example.org
IdentityFile ~/.ssh/id_rsa
Now let's provision our server. As our host doesn't have user deployer, we are going to override remote_user
for
provision via -o remote_user=root
.
dep provision -o remote_user=root
Deployer will ask you a few questions during provisioning: php version, database type, etc. Next Deployer will configure
our server and create the deployer user. Provision takes around 5 minutes and will install everything we need to run a
website. A new website will be configured at deploy_path
.
To deploy your application, run the following command:
./vendor/bin/dep deploy
Note
If deploy failed, Deployer will print an error message and which command was unsuccessful. Most likely we need to configure the correct database credentials in.env
file or similar.
Deployer can be used in CI/CD pipelines.
See more
Read more about it in Deployer CI/CD section.
Docker allows for easy deployment, scaling, and management of application containers. It also makes it easy to replicate the production environment and test applications locally, which makes it easier to find and fix issues.
This method involves building a Docker image of the application, and then running the image on the production server.
Here is an example of a Dockerfile
that can be used to build a Docker image for a Spiral application:
# This example will work with application root directory as docker context
FROM php:8.2-cli-alpine3.17 as backend
RUN --mount=type=bind,from=mlocati/php-extension-installer:1.5,source=/usr/bin/install-php-extensions,target=/usr/local/bin/install-php-extensions \
install-php-extensions opcache zip xsl dom exif intl pcntl bcmath sockets && \
apk del --no-cache ${PHPIZE_DEPS} ${BUILD_DEPENDS}
WORKDIR /app
ENV COMPOSER_ALLOW_SUPERUSER=1
COPY --from=composer:2.3 /usr/bin/composer /usr/bin/composer
COPY ./composer.* .
RUN composer config --no-plugins allow-plugins.spiral/composer-publish-plugin false && \
composer install --optimize-autoloader --no-dev
COPY --from=spiralscout/roadrunner:latest /usr/bin/rr /app
EXPOSE 8080/tcp
COPY ./ .
CMD ./rr serve -c .rr.yaml
You can build the image by running the following command:
docker build . -t my-application:latest
After the image is built, you need to push it to a Docker registry or hub.
docker push my-application:latest
You can also tag the image with a version number, such as my-application:1.0
.
docker build . -t my-application:latest -t my-application:1.0docker push my-application:latestdocker push my-application:1.0
One of the advantages of using Docker is that you can manage environment variables for your application through the
docker-compose
file and external .env
files, rather than having to create .env
files inside the container.
Here is an example of a docker-compose.yml
file that can be used to start the application:
version: '3'
services:
app:
image: my-application:1.0
ports:
- "8080:8080"
environment:
- DEBUG=false
- APP_ENV=production
- ...
...
You can start the application by running the following command:
docker-compose up -d
and stop it by running
docker-compose down
A popular way to deploy an application is by using source control. This involves connecting your production server to a remote repository, such as Git or SVN, and then deploying the application by pulling in the latest changes from the repository.
git pull origin master
to pull in the latest changes from the remote repositorycomposer install --optimize-autoloader --no-dev
to install the dependencies./rr reset
Warning:
Using this strategy without automation can make the deployment process more time-consuming and prone to errors. Every time a deployment is needed, developers would need to manually pull the code from the repository and run any necessary console commands in a specific order. This increases the risk of human error, such as running the commands in the wrong order or forgetting to run a command, which can lead to issues with the application.
One of the simplest ways to deploy a Spiral application is through basic file transfer. This can be done using FTP, or SCP. The process involves uploading the application files from your local machine to the production server.
composer install --optimize-autoloader --no-dev
to install the dependencies./rr reset
Warning:
This strategy is considered less efficient and less secure than other methods. It can take a long time to upload all the files manually, and there is a risk of data loss or corruption if the transfer is interrupted. Additionally, it can be difficult to keep track of which version of the code is currently on the production server, making it hard to rollback in case of errors.