In this example we are going to deploy a Dockerised Symfony application to production server with Ansible and Jenkins. It is always tricky when it comes to working with production environment so the Docker related files are a bit different compared to development environment as well as other build files. You will see the difference below.


Perquisites for Jenkins server


Make sure you covered points below.



We have Jenkins and production with details below.



We also have a MySQL server (instead of a container) with details below.


DB_VERS=5.7.24
DB_HOST=192.168.99.20
DB_PORT=3306
DB_NAME=sport
DB_USER=inanzzz
DB_PASS=123123

Perquisites for Production server


Prepare SSH GitHub integration and make sure it can run docker commands.


Flow


The Jenkins and GitHub integration is already set up so when I merge a PR to master branch, GitHub communicates with Jenkins where deployment pipeline runs.


Alternative: This example makes use of git to clone repository into the production server and build the docker images/containers. However, if you prefer copying the application into container images and eliminate repository cloning steps, you can read this post. In such case, your Jenkinsfile would change to build and push images to Docker Hub. Afterwards, a stage would need to copy relevant docker files over to production server then run docker pull/build/up commands.


  1. Jenkins checkout to master branch.

  2. Ansible SSH into production server and start deployment process.

    1. Create the application directory.

    2. Clone application repository.

    3. Copy vault encrypted .env file over.

    4. Bring the application up.

    5. Install secure-delete package.

    6. Securely delete everything copied over.

Perquisites for MySQL server


Steps below are one off and must be done before everything.


Allow external connections


Change bind-address as follows.


mysql-server$ cat /etc/mysql/my.cnf | grep bind-address
#bind-address = 127.0.0.1
bind-address = 0.0.0.0

Login to MySQL server with mysql-server$ mysql -u root -proot and do the followings.


New database and user with permissions


Create a new database called sport.


mysql> CREATE DATABASE `sport` CHARACTER SET `UTF8mb4` COLLATE `utf8mb4_unicode_ci`;

mysql> SHOW databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sport |
+--------------------+

Create a new user inanzzz with all permissions on sport database.


mysql> USE mysql;

mysql> CREATE USER 'inanzzz'@'%' IDENTIFIED BY '123123';
mysql> GRANT ALL PRIVILEGES ON sport.* TO 'inanzzz'@'%' WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;

mysql> SELECT user, host FROM user;
+------------------+-----------+
| user | host |
+------------------+-----------+
| inanzzz | % |
| root | 127.0.0.1 |
| root | ::1 |
| root | localhost |
+------------------+-----------+

mysql> SHOW GRANTS FOR 'inanzzz'@'%';
+--------------------------------------------------------------------------------------------------------+
| Grants for inanzzz@% |
+--------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'inanzzz'@'%' IDENTIFIED BY PASSWORD '*E56A114692FE0DE073F9A1DD68A00EEB9703F3F1' |
| GRANT ALL PRIVILEGES ON `sport`.* TO 'inanzzz'@'%' WITH GRANT OPTION |
+--------------------------------------------------------------------------------------------------------+

Structure


I am going to list dev environment files as well just for you to compare with prod environment files so that you know how they differ. The major differences are in Dockerfile, Makefile and docker-compose.yml files.


.
├── bin
│   └── ...
├── cicd
│   ├── merge
│   │   └── master
│   │   └── Jenkinsfile
│   └── provision
│   └── prod
│   ├── hosts.yml
│   └── site.yml
├── composer.json
├── composer.lock
├── config
│   └── ...
├── docker
│   ├── dev
│   │   ├── docker-compose.yml
│   │   ├── Makefile
│   │   ├── nginx
│   │   │   ├── app.conf
│   │   │   ├── Dockerfile
│   │   │   └── nginx.conf
│   │   └── php
│   │   ├── Dockerfile
│   │   ├── php.ini
│   │   └── www.conf
│   └── prod
│   ├── docker-compose.yml
│   ├── Makefile
│   ├── nginx
│   │   ├── app.conf
│   │   ├── Dockerfile
│   │   └── nginx.conf
│   └── php
│      ├── Dockerfile
│      ├── php.ini
│      └── www.conf
├── .dockerignore
├── .env
├── .gitignore
├── public
│   └── ...
├── Readme.md
├── src
│   └── ...
├── symfony.lock
├── var
│   └── ...
└── vendor
   └── ...

Dev files


docker/dev/docker-compose.yml


version: "3.4"

services:

football_mysql:
image: "mysql:5.7.24"
hostname: "football-mysql"
command: "--default-authentication-plugin=mysql_native_password"
environment:
MYSQL_ROOT_PASSWORD: "root"
PS1: "\\u@\\h:\\w\\$$ "

football_php:
build:
context: "./php"
hostname: "football-php"
volumes:
- "../..:/app:consistent"
depends_on:
- "football_mysql"
environment:
PS1: "\\u@\\h:\\w\\$$ "
sysctls:
- "net.core.somaxconn=1000"

football_nginx:
build:
context: "./nginx"
hostname: "football-nginx"
ports:
- "1080:80"
volumes:
- "../..:/app:consistent"
depends_on:
- "football_mysql"
- "football_php"
environment:
PS1: "\\u@\\h:\\w\\$$ "

docker/dev/Makefile


PHP_SERVICE := football_php

build:
@docker-compose build

up:
@docker-compose up -d

composer:
@docker-compose exec -T $(PHP_SERVICE) composer install

database:
@docker-compose exec -T $(PHP_SERVICE) bin/console doctrine:database:create --if-not-exists
@docker-compose exec -T $(PHP_SERVICE) bin/console doctrine:migrations:migrate --no-interaction
@docker-compose exec -T $(PHP_SERVICE) bin/console doctrine:schema:validate

clean:
@docker-compose down
@docker system prune --volumes --force

config:
@docker-compose config

init:
@make build
@make up
@make composer
@make database

docker/dev/nginx/app.conf


server {
listen 80 default_server;

server_name localhost;

root /app/public;

location / {
try_files $uri /index.php$is_args$args;
}

location ~ ^/index\.php(/|$) {
fastcgi_pass football_php:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_hide_header X-Powered-By;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_param HTTP_X_REQUEST_ID $request_id;
internal;
}

location ~ \.php$ {
return 404;
}

error_log /var/log/nginx/app_error.log;
access_log /var/log/nginx/app_access.log;
}

docker/dev/nginx/nginx.conf


user nginx;

worker_processes 2;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
worker_connections 2048;
use epoll;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
server_tokens off;

add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection '1; mode=block';
add_header X-Frame-Options DENY;
add_header Strict-Transport-Security 'max-age=31536000; includeSubdomains; preload';
add_header 'Referrer-Policy' 'no-referrer-when-downgrade';

types_hash_max_size 2048;
gzip on;

include /etc/nginx/conf.d/*.conf;
}

docker/dev/nginx/Dockerfile


FROM nginx:1.15.8-alpine

RUN rm -rf /var/cache/apk/* \
/var/lib/apt/lists/*

COPY app.conf /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/nginx.conf

docker/dev/php/php.ini


[PHP]
date.timezone=UTC
log_errors=On
error_reporting=E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors=Off
max_execution_time=60
memory_limit=256M

[opcache]
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
realpath_cache_size=4096K
realpath_cache_ttl=600

docker/dev/php/www.conf


[global]
daemonize=no

[www]
user=www-data
group=www-data

listen=football_nginx:9000
listen.backlog=1000 # This is optional but important for production so read what I wrote to docker-compose.yml file section.

pm=dynamic
pm.max_children=40
pm.start_servers=2
pm.min_spare_servers=2
pm.max_spare_servers=4
pm.max_requests=500

docker/dev/php/Dockerfile


FROM php:7.2.13-fpm-alpine3.8

WORKDIR /app

RUN apk update \
&& apk add --no-cache $PHPIZE_DEPS \
git \
zip \
unzip \
&& docker-php-ext-install \
opcache \
pdo_mysql \
&& docker-php-ext-enable \
opcache \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& rm -rf \
/var/cache/apk/* \
/var/lib/apt/lists/*

COPY php.ini /usr/local/etc/php/conf.d/php.override.ini
COPY www.conf /usr/local/etc/php-fpm.d/www.conf

CMD ["php-fpm", "--nodaemonize"]

Prod files


docker/prod/docker-compose.yml


If we don't use a different name for "named volume" for each deployment, the changes we do in application code won't be available in production. Hence reason, I am suffixing it with BUILD_ID environment variable which comes from Jenkins. Ansible playbook passes it to Makefile then Makefile passes it to docker-compose.yml. Also, I am baking the application source code into "php container" called football_php and let nginx service access it through the "named volume".


Important note: The net.core.somaxconn is optional but important. This is a Linux Kernel parameter that specifies the maximum backlogged TCP/IP sockets. The default value is 128. Based on our changes, the Kernel will allow maximum of 1000 connections to be “backlogged” at a time. The value cannot exceed maximum limit of USHRT_MAX constant which is 65535. The same value goes with the listen.backlog option in /usr/local/etc/php-fpm.d/www.conf file. You can run sysctl net.core.somaxconn command in PHP-FPM container to verify it. In short, if you are expecting too many concurrent requests then it is best to keep the value high. I've sent total of 10000:1000 (total:concurrent) requests with $ ab -n 10000 -c 1000 http://0.0.0.0:80/ command, all passed. If I reduce the value to 500 then about 300 requests fail. Another example, Redis would require many connections so it would need a high number as well.


version: "3.4"

services:

football_php:
build:
context: "../.."
dockerfile: "docker/prod/php/Dockerfile"
hostname: "football-php"
volumes:
- "source_code:/app:consistent"
environment:
PS1: "\\u@\\h:\\w\\$$ "
sysctls:
- "net.core.somaxconn=1000"
env_file:
- "../../.env"

football_nginx:
build:
context: "./nginx"
hostname: "football-nginx"
ports:
- "80:80"
volumes:
- "source_code:/app:consistent"
depends_on:
- "football_php"
environment:
PS1: "\\u@\\h:\\w\\$$ "

volumes:
source_code:
name: "football_source_code_${BUILD_ID}"

docker/prod/Makefile


build:
@docker-compose build

up:
@docker-compose up -d

clean:
@docker system prune --volumes --force

config:
@docker-compose config

init:
@make build
@make up
@make clean

docker/prod/nginx/app.conf


Don't ignore the SSL certificates and the port 443 like I did here in production!


server {
listen 80 default_server;

server_name localhost;

root /app/public;

location / {
try_files $uri /index.php$is_args$args;
}

location ~ ^/index\.php(/|$) {
fastcgi_pass football_php:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_hide_header X-Powered-By;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
fastcgi_param HTTP_X_REQUEST_ID $request_id;
internal;
}

location ~ \.php$ {
return 404;
}

error_log /var/log/nginx/app_error.log;
access_log /var/log/nginx/app_access.log;
}

docker/prod/nginx/nginx.conf


user nginx;

worker_processes 2;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
worker_connections 2048;
use epoll;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
server_tokens off;

add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection '1; mode=block';
add_header X-Frame-Options DENY;
add_header Strict-Transport-Security 'max-age=31536000; includeSubdomains; preload';
add_header 'Referrer-Policy' 'no-referrer-when-downgrade';

types_hash_max_size 2048;
gzip on;

include /etc/nginx/conf.d/*.conf;
}

docker/prod/nginx/Dockerfile


FROM nginx:1.15.8-alpine

WORKDIR /app

RUN rm -rf /var/cache/apk/* \
/var/lib/apt/lists/*

COPY app.conf /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/nginx.conf

docker/prod/php/php.ini


[PHP]
date.timezone=UTC
log_errors=On
error_reporting=E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors=Off
max_execution_time=60
memory_limit=256M

[opcache]
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
realpath_cache_size=4096K
realpath_cache_ttl=600

docker/prod/php/www.conf


[global]
daemonize=no

[www]
user=www-data
group=www-data

listen=football_nginx:9000
listen.backlog=1000 # This is optional but important for production so read what I wrote to docker-compose.yml file section.

pm=dynamic
pm.max_children=40
pm.start_servers=2
pm.min_spare_servers=2
pm.max_spare_servers=4
pm.max_requests=500

docker/prod/php/Dockerfile


FROM php:7.2.13-fpm-alpine3.8

WORKDIR /app

RUN apk update \
&& apk add --no-cache $PHPIZE_DEPS \
git \
zip \
unzip \
&& docker-php-ext-install \
opcache \
pdo_mysql \
&& docker-php-ext-enable \
opcache \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& rm -rf \
/var/cache/apk/* \
/var/lib/apt/lists/*

COPY . /app

COPY docker/prod/php/php.ini /usr/local/etc/php/conf.d/php.override.ini
COPY docker/prod/php/www.conf /usr/local/etc/php-fpm.d/www.conf

RUN set -xe \
&& composer install --no-dev --no-scripts --no-suggest --no-interaction --prefer-dist --optimize-autoloader \
&& composer dump-autoload --no-dev --optimize --classmap-authoritative \
&& bin/console cache:clear \
&& bin/console cache:warm \
&& bin/console doctrine:migrations:migrate --no-interaction

CMD ["php-fpm", "--nodaemonize"]

CICD files


cicd/merge/master/Jenkinsfile


pipeline {
agent any

options {
skipDefaultCheckout(true)
}

stages {
stage('Git') {
steps {
echo '> Checking out the Git version control ...'
checkout scm
}
}
stage('Deploy') {
steps {
echo '> Deploying the application ...'
ansiblePlaybook(
vaultCredentialsId: 'AnsibleVault',
inventory: 'cicd/provision/prod/hosts.yml',
playbook: 'cicd/provision/prod/site.yml'
)
}
}
}
}

cicd/provision/prod/hosts.yml


You could encrypt this with Ansible vault if you wanted.


all:
hosts:
production:
ansible_connection: ssh
ansible_user: vagrant
ansible_host: 192.168.99.30
ansible_port: 22

cicd/provision/prod/site.yml


You could prepare MySQL server here if you wanted and also use some environment variables.


---
# All tasks below are run in "production" server.

- name: Deploy application to the "production" server
hosts: production
remote_user: vagrant
become: yes
tasks:

- name: Create application directory
file:
path: /home/vagrant/football
state: directory
owner: vagrant
group: vagrant

- name: Clone application repository
git:
repo: git@github.com:inanzzz/football.git
dest: /home/vagrant/football
clone: yes
force: yes
recursive: yes
version: master
accept_hostkey: yes
key_file: /home/vagrant/.ssh/id_rsa
become_user: vagrant

- name: Copy secret .env file over
copy:
src: /var/lib/jenkins/app-secrets/sport/.env
dest: /home/vagrant/football/.env
owner: vagrant
group: vagrant
no_log: true

- name: Bring application up
make:
chdir: /home/vagrant/football/docker/prod
target: init
params:
BUILD_ID: "{{ lookup('env','BUILD_ID') }}"

- name: Install "secure-delete" package
apt:
name: secure-delete
state: present

- name: Secure deleting application folder
command: srm -vzr /home/vagrant/football

Other files


.dockerignore


cicd/
config/packages/dev/
config/packages/test/
docker/dev/
var/
vendor/
.dockerignore
.gitignore
Readme.md

.git/
.idea/
.DS_Store/

.env


APP_ENV=dev
APP_SECRET=1c29c11fe70df5af758f775c6e3d03ef
DB_HOST=football_mysql
DB_PORT=3306
DB_NAME=football
DB_USER=root
DB_PASS=root
DB_VERS=5.7.24

config/packages/doctrine.yaml


doctrine:
dbal:
host: '%env(string:DB_HOST)%'
port: '%env(int:DB_PORT)%'
dbname: '%env(string:DB_NAME)%'
user: '%env(string:DB_USER)%'
password: '%env(string:DB_PASS)%'
driver: 'pdo_mysql'
server_version: '%env(string:DB_VERS)%'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
orm:
...

DefaultContainer.php


class EnvironmentController
{
private $envFile;

public function __construct(
string $envFile # This refers to .env fine in the project root
) {
$this->envFile = $envFile;
}

public function index(): Response
{
$envFile = file_get_contents($this->envFile);

echo trim($envFile).PHP_EOL;
echo '------------------------------------------'.PHP_EOL;
foreach (['APP_ENV', 'APP_SECRET', 'DB_VERS', 'DB_HOST', 'DB_PORT', 'DB_NAME', 'DB_USER', 'DB_PASS'] as $env) {
echo $env.':'.getenv($env).PHP_EOL;
}
echo '------------------------------------------'.PHP_EOL;
print_r($_ENV);

return new Response();
}
}

Ansible Vault encryption


The .env file for production environment contains secret/important values so we need to create it on Jenkins server and encrypt it with ansible-vault command. When the deployments kicks in, it will get decrypted behind the scene (at stage('Deploy')) and transferred to production server. The values will be set as environment variables in php container and our Symfony application will use them instead of the same variables set in .env file. This is how it works, environment variables have priority!


jenkins@jenkins-server:$ mkdir -p ~/app-secrets/sport
jenkins@jenkins-server:$ ansible-vault create ~/app-secrets/sport/.env
New Vault password: # the password is same as the one set in Jenkins credentials section
Confirm New Vault password: # repeat

jenkins@jenkins-server:$ cat ~/app-secrets/sport/.env
$ANSIBLE_VAULT;1.1;AES256
31396662396139623232346537373466616234636535373534333061353766313263623035313833
3436356166613638343366663933623137343833373134660a663334333139393632653233656333
66626333623134623066663565323761323934663835376439333231313733363636633539393630
6164326464646337360a353133666335303734303532623866613633623932613737616635383631
39666432626562383932363363326664306636356236666136353661356632613337333762323166
63653234613536616432663363373265393732313035333461396533336539373638666561633135
31316430633266356562313362366564343935313231383561336636656133666561366239343638
63396364636436353932313638653532336335643737383436623737356637636136646239323831
37363639363964623762303361383738303436313133643333653063333239663630613566626135
36306130346331633931373439633632653930656137303261633339316335656539613536643933
66393830393263323266656634646465306338383062386535393538363964646561353533313239
32383538356439613030313134653861393161363639643835633564313137656566623638366534
3632

The actual content is shown below.


APP_ENV=prod
APP_SECRET=91f6e527cdd2e73e929a6aaaea8036c630b025a7
DB_VERS=5.7.24
DB_HOST=192.168.99.20
DB_PORT=3306
DB_NAME=sport
DB_USER=inanzzz
DB_PASS=123123

Testing production pipeline


Assume that we have merged a PR info master branch.


Running on Jenkins in /var/lib/jenkins/workspace/sport-merge-master

[Pipeline] Start of Pipeline
> Checking out the Git version control ...
using GIT_SSH to set credentials
Checking out Revision 11d8e7e0a0584ue4112d4b37ee2f0a2df18abar5 (origin/master)

> Deploying the application ...
[football-merge-master] $ ansible-playbook cicd/provision/prod/site.yml
-i cicd/provision/prod/hosts.yml
--vault-password-file /var/lib/jenkins/workspace/football-merge-master/vault2614692457220871566.password

PLAY [Deploy application to the "production" server] ***************************

TASK [Gathering Facts] *********************************************************
ok: [production]

TASK [Create application directory] ********************************************
changed: [production]

TASK [Clone application repository] ********************************************
changed: [production]

TASK [Copy secret .env file over] **********************************************
changed: [production]

TASK [Bring application up] ****************************************************
changed: [production]

TASK [Install "secure-delete" package] *****************************************
ok: [production]

TASK [Secure deleting application folder] **************************************
changed: [production]

PLAY RECAP *********************************************************************
production : ok=7 changed=5 unreachable=0 failed=0

[Pipeline] End of Pipeline
Finished: SUCCESS


Checking production server


As you can see the /home/vagrant/sport we created at the beginning has been deleted at the end of the deployment process.


vagrant@production:~$ ls -l
total 0

vagrant@production:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
prod_football_php latest 17ebe027a0cb 34 minutes ago 310MB
prod_football_nginx latest f997f8d3c442 About an hour ago 16.1MB
nginx 1.15.8-alpine b411e34b4606 3 months ago 16.1MB
mysql 5.7.24 ba7a93aae2a8 4 months ago 372MB
php 7.2.13-fpm-alpine3.8 262e46e3d43c 5 months ago 77.7MB

vagrant@production:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aae23b67b036 prod_football_nginx "nginx -g 'daemon of…" 35 minutes ago Up 35 minutes 0.0.0.0:80->80/tcp prod_football_nginx_1
d201791f9b9b prod_football_php "docker-php-entrypoi…" 35 minutes ago Up 35 minutes 9000/tcp prod_football_php_1

vagrant@production:~$ docker volume ls
DRIVER VOLUME NAME
local football_source_code_17

vagrant@production:~$ docker inspect football_source_code_17
[
{
"CreatedAt": "2019-05-25T17:11:37+01:00",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "prod",
"com.docker.compose.version": "1.21.2",
"com.docker.compose.volume": "football_source_code_17"
},
"Mountpoint": "/var/lib/docker/volumes/football_source_code_17/_data",
"Name": "football_source_code_17",
"Options": null,
"Scope": "local"
}
]

vagrant@production:~$ sudo ls -l /var/lib/docker/volumes/football_source_code_17/_data
drwxr-xr-x 2 root root 4096 May 25 17:11 bin
-rw-r--r-- 1 root root 1444 May 25 17:10 composer.json
-rw-r--r-- 1 root root 118858 May 25 17:10 composer.lock
drwxr-xr-x 4 root root 4096 May 25 17:11 config
drwxr-xr-x 3 root root 4096 May 25 17:11 docker
drwxr-xr-x 2 root root 4096 May 25 17:11 public
drwxr-xr-x 8 root root 4096 May 25 17:11 src
-rw-r--r-- 1 root root 5998 May 25 17:10 symfony.lock
drwxr-xr-x 4 root root 4096 May 25 17:11 var
drwxr-xr-x 11 root root 4096 May 25 17:11 vendor

Checking application


vagrant@production:~$ curl -i http://0.0.0.0:80 # or http://0.0.0.0 | http://localhost
# or from outside with http://192.168.99.30

APP_ENV=prod
APP_SECRET=91f6e527cdd2e73e929a6aaaea8036c630b025a7
DB_VERS=5.7.24
DB_HOST=192.168.99.20
DB_PORT=3306
DB_NAME=sport
DB_USER=inanzzz
DB_PASS=123123
------------------------------------------
APP_ENV:prod
APP_SECRET:91f6e527cdd2e73e929a6aaaea8036c630b025a7
DB_VERS:5.7.24
DB_HOST:192.168.99.20
DB_PORT:3306
DB_NAME:sport
DB_USER:inanzzz
DB_PASS:123123
------------------------------------------
Array
(
[PHP_EXTRA_CONFIGURE_ARGS] => --enable-fpm --with-fpm-user=www-data --with-fpm-group=www-data --disable-cgi
[HOSTNAME] => football-php
[DB_PORT] => 3306
[PHP_INI_DIR] => /usr/local/etc/php
[SHLVL] => 1
[HOME] => /home/www-data
[DB_NAME] => sport
[PS1] => \u@\h:\w\$
[PHP_LDFLAGS] => -Wl,-O1 -Wl,--hash-style=both -pie
[PHP_CFLAGS] => -fstack-protector-strong -fpic -fpie -O2
[PHP_MD5] =>
[PHP_VERSION] => 7.2.13
[GPG_KEYS] => 1729F83938DA44E27BA0F4D3DBDB397470D12172 B1B44D8F021E4E2D6021E995DC9FF8D3EE5AF27F
[PHP_CPPFLAGS] => -fstack-protector-strong -fpic -fpie -O2
[PHP_ASC_URL] => https://secure.php.net/get/php-7.2.13.tar.xz.asc/from/this/mirror
[PHP_URL] => https://secure.php.net/get/php-7.2.13.tar.xz/from/this/mirror
[APP_SECRET] => 91f6e527cdd2e73e929a6aaaea8036c630b025a7
[PATH] => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
[DB_PASS] => 123123
[APP_ENV] => prod
[PHPIZE_DEPS] => autoconf dpkg-dev dpkg file g++ gcc libc-dev make pkgconf re2c
[PWD] => /app
[PHP_SHA256] => 14b0429abdb46b65c843e5882c9a8c46b31dfbf279c747293b8ab950c2644a4b
[DB_HOST] => 192.168.99.20
[DB_USER] => inanzzz
[DB_VERS] => 5.7.24
[USER] => www-data
[HTTP_ACCEPT] => */*
[HTTP_HOST] => 0.0.0.0
[HTTP_USER_AGENT] => curl/7.38.0
[HTTP_X_REQUEST_ID] => a9fa864522fa78ee7b3ffb5ec1ddf730
[SCRIPT_FILENAME] => /app/public/index.php
[REDIRECT_STATUS] => 200
[SERVER_NAME] => localhost
[SERVER_PORT] => 80
[SERVER_ADDR] => 172.19.0.4
[REMOTE_PORT] => 43109
[REMOTE_ADDR] => 172.19.0.1
[SERVER_SOFTWARE] => nginx/1.15.8
[GATEWAY_INTERFACE] => CGI/1.1
[REQUEST_SCHEME] => http
[SERVER_PROTOCOL] => HTTP/1.1
[DOCUMENT_ROOT] => /app/public
[DOCUMENT_URI] => /index.php
[REQUEST_URI] => /env
[SCRIPT_NAME] => /index.php
[CONTENT_LENGTH] =>
[CONTENT_TYPE] =>
[REQUEST_METHOD] => GET
[QUERY_STRING] =>
[FCGI_ROLE] => RESPONDER
[PHP_SELF] => /index.php
[REQUEST_TIME_FLOAT] => 1558803124.4202
[REQUEST_TIME] => 1558803124
[argv] => Array
(
)

[argc] => 0
[APP_DEBUG] => 0
)