Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Composer & Cache Permissions

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $9.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Look back at the Symfony Deployment article: we now have a parameters file! Woo! Next, we need to run composer install - which was the original reason the site didn't work - and then warm up the Symfony cache. We're really close to a functional site. We won't need to dump the Assetic assets - we're not using Assetic. But we will need to do some asset processing later.

Running composer install

Let's add a new task to run composer install. In the hook file, add "Install Composer deps". Use the composer module and tell it to run the install command. We also need to set the working_dir: use {{ ansistrano_release_path.stdout }}:

---
- name: Set up infrastructure-related parameters
... lines 3 - 6
- name: Install Composer dependencies
composer:
command: install
working_dir: '{{ ansistrano_release_path.stdout }}'

Perfect! One gotcha with the composer module is that, by default, it runs composer install --no-dev. That means that your require-dev dependencies in composer.json will not be downloaded:

71 lines composer.json
{
... lines 2 - 30
"require-dev": {
"sensio/generator-bundle": "^3.0",
"symfony/phpunit-bridge": "^3.0",
"doctrine/data-fixtures": "^1.1",
"hautelook/alice-bundle": "^1.3"
},
... lines 38 - 69
}

For production, that's a good thing: it will give you a small performance boost. Just make sure that you're not relying on anything in those packages!

Also, in Symfony 3, if you use --no-dev, then some of the post-install Composer tasks will fail, because they need those dependencies. To fix that, we need to set an environment variable: SYMFONY_ENV=prod.

No problem! In deploy.yml, add a new key called environment. And below, SYMFONY_ENV set to prod:

---
- hosts: aws
... lines 3 - 27
environment:
SYMFONY_ENV: prod
... lines 30 - 33

Thanks to this, the Composer post-install tasks will not explode. And that's good... it's not great when your deployment explodes.

Oh, and important note: for this all to work, Composer must be already installed on your server. We did that in our provision playbook.

Clearing & Warming Cache

Before we try this, let's tackle one last thing: clearing the Symfony cache... which basically means running two console commands.

To make this easier, in deploy.yml, add a new variable: release_console_path. Copy the ansistrano_release_path.stdout variable and paste it: {{ ansistrano_release_path.stdout }}/bin/console:

---
- hosts: aws
... lines 3 - 14
vars:
release_console_path: "{{ ansistrano_release_path.stdout }}/bin/console"
# Ansistrano vars
... lines 19 - 35

Cool! Back in the hook file, add a new task to clear the cache. Use the command module to simply say {{ release_console_path }} cache:clear --no-warmup --env=prod:

... lines 1 - 6
- name: Install Composer dependencies
... lines 8 - 11
- name: Clear the cache
command: '{{ release_console_path }} cache:clear --no-warmup --env=prod'
... lines 14 - 17

That's basically the command that you see in the docs.

If you're not familiar with the --no-warmup flag, it's important. In Symfony 4, instead of running cache:clear and expecting it to clear your cache and warm up your cache, cache:clear will only clear your cache. Then, you should use cache:warmup separately to warm it up. By passing --no-warmup, we're imitating the Symfony 4 behavior so that we're ready.

Add the second task: "Warm up the Cache". Copy the command, but change it to just cache:warmup --env=prod:

... lines 1 - 11
- name: Clear the cache
command: '{{ release_console_path }} cache:clear --no-warmup --env=prod'
- name: Warm up the cache
command: '{{ release_console_path }} cache:warmup --env=prod'

Now, technically, since the cache/ directory is not shared between deploys, we don't really need to run cache:clear: it will always be empty at this point! But, I'll keep it.

Ok! Phew! I think we've done everything. Let's deploy! Find your local terminal and run the playbook:

ansible-playbook -i ansible/hosts.ini ansible/deploy.yml --ask-vault-pass

Use beefpass as the vault password and deploy to master. Then... wait impatiently! Someone fast forward, please!

Yes! No errors! On the server, move out of current/ and then back in. Check it out! Our vendor/ directory is filled with goodies!

Fixing the File Permissions

Moment of truth: try the site again: mootube.example.com. Bah! It still doesn't work. Let's find out why. On the server, tail the log file:

sudo tail /var/log/nginx/mootube.example.com_error.log

Ooooh:

PHP Fatal error: The stream or file "var/logs/prod.log" could not be opened

Of course! We have permissions problems on the var/ directory! Fixing this is actually a very interesting topic. There is an easy way to fix this... and a more complex, but more secure way.

For now, let's use the simple way: I really want our app to work! Add a new task: "Setup directory permissions for var". Use the file module. But, quickly, go back to deploy.yml and make another variable: release_var_path set to the same path {{ ansistrano_release_path.stdout }}/var:

---
- hosts: aws
... lines 3 - 14
vars:
release_console_path: "{{ ansistrano_release_path.stdout }}/bin/console"
release_var_path: "{{ ansistrano_release_path.stdout }}/var"
... lines 18 - 36

Now, back in after-symlink-shared.yml, set the path to {{ release_var_path }}, state to directory, mode to 0777 and recurse: true:

... lines 1 - 17
- name: Setup directory permissions for var/
file:
path: "{{ release_var_path }}"
state: directory
mode: 0777
recurse: true

On deploy, this will make sure that the directory exists and is set to 777. That's not the best option for security... but it should get things working!

Deploy one more time:

ansible-playbook -i ansible/hosts.ini ansible/deploy.yml --ask-vault-pass

Type beefpass, deploy to master... and watch the magic. I can see the new directory permissions task... and it finishes.

Refresh the site! Eureka! Yea, it's still a 500 error, but this comes from Symfony! Symfony is running! Change the URL to http://mootube.example.com/about. It works! Yea, it's super ugly - we need to do some work with our assets - but it does work. The homepage is broken because our database isn't setup. But this static page proves our deploy is functional! Victory!

Now, let's smooth out the missing details... like the insecure permissions, the database and our assets... because this site is horrible to look at!

Leave a comment!

6
Login or Register to join the conversation
Cesar Avatar

Hello guys:

I just upgrade my Symfony app to 4.3 version from 3.4. Everything is working in local but I have problems deploying by Ansistrano.

The task Install Composer dependencies is exploting and appears the message "Executing script cache:clear [KO]", Environment variable not found: \"DATABASE_URL\". But, I put that variable in a file .env.prod.local that I put in the ansistrano_release_path.stdout using the synchronize module of Ansible. Please, can you tell me any suggestion about how to fix this?

Thanks.

Cesar

Reply

Hey Cesar

Have you tried to put that env var on .env with a dummy value and then override it on .env.local file?

Cheers!

Reply
Default user avatar
Default user avatar Chris | posted 5 years ago | edited

In another project I currently use the ansistrano workflow (and I have tested this additionally with your finished downloaded version).

When I start to deploy I get this message:

An error occurred when executing the \"'cache:clear --no-warmup'\" command: \n PHP Fatal error: Uncaught Error: Class 'Liip\ImagineBundle\LiipImagineBundle' not found in /var/www/project/releases/20180123144814Z/app/AppKernel.php:23 Stack trace: #0 /var/www/project/releases/20180123144814Z/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php(448): AppKernel->registerBundles()

In deploy.yml I set


environment:
    SYMFONY_ENV: prod

In after-symlink-shared.yml:


- name: Clear the cache
  command: "{{ release_console_path }} cache:clear --no-warmup --env=prod"

- name: Warm up the cache
  command: "{{ release_console_path }} cache:warmup --env=prod"

In composer.json I add:


    "require": {
        "liip/imagine-bundle": "^1.9"
    }

Can you please help me to get my deployment going? At this point after researching for hours I'm clueless what to do next.

Thank you very much :)

Reply
Default user avatar

I manged it to install liip/imagine-bundle onto my ec2 server,

Installing jms/serializer-bundle (2.3.1): Loading from cache - Installing imagine/imagine (v0.7.1): Loading from cache - Installing liip/imagine-bundle (1.9.1): Loading from cache -

but now I get

[Symfony\\Component\\Config\\Exception\\FileLoaderLoadException] \n Class Doctrine\\Bundle\\FixturesBundle\\Fixture not found in /var/www/project/ \n releases/20180124103155Z/app/config/services.yml (which is being imported f \n rom \"/var/www/project/releases/20180124103155Z/app/config/config.yml\").

After digging into it, I used this approach: https://github.com/doctrine/DoctrineFixturesBundle/issues/225#issuecomment-352538507

If there is a better solution, please let me know.

Have a nice day,
Chris

Reply

Hey Chris!

I'm glad you figured out the first issue :). What was the fix? Your original code looked good to me, so I was also surprised that the bundle wasn't originally being installed on your server.

About your second issue, this is fascinating! I'm afraid that I caused this bug with the new DoctrineFixturesBundle system :). I've already added a comment on the issue you linked and created an issue on Symfony's main repository. Basically, I think we have a "bug", or a situation we hadn't thought of. But the solution is 100% fine. In fact, it's basically what the "fix" would do internally - so there's no downside to that fix, other than it's annoying that you need to write all that code :).

Cheers!

Reply
Default user avatar

It's weird and to be honest I actually don't know what the problem was. It's pretty inconclusive for me how this problem could happen to me.

For the second problem it's nice to see that you are fixing this issue. Thanks for the fast reply. With it it's assured that this approach is valid and the error is not caused through a mistake of my own.

Many greetings and keep up the good work!
Chris

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

While the fundamentals of Ansistrano haven't changed, this tutorial is built using Symfony 3, which has significant differences versus Symfony 4 and later.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "doctrine/doctrine-bundle": "^1.6", // 1.6.8
        "doctrine/orm": "^2.5", // v2.7.2
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "sensio/distribution-bundle": "^5.0.19", // v5.0.20
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
        "symfony/monolog-bundle": "^3.1.0", // v3.1.0
        "symfony/polyfill-apcu": "^1.0", // v1.4.0
        "symfony/swiftmailer-bundle": "^2.3.10", // v2.6.3
        "symfony/symfony": "3.3.*", // v3.3.5
        "twig/twig": "^1.0||^2.0", // v1.34.4
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
        "predis/predis": "^1.1", // v1.1.1
        "composer/package-versions-deprecated": "^1.11" // 1.11.99
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.6
        "symfony/phpunit-bridge": "^3.0", // v3.3.5
        "doctrine/data-fixtures": "^1.1", // 1.3.3
        "hautelook/alice-bundle": "^1.3" // v1.4.1
    }
}

What Ansible libraries does this tutorial use?

# ansible/requirements.yml
-
    src: DavidWittman.redis
    version: 1.2.4
-
    src: ansistrano.deploy
    version: 2.7.0
-
    src: ansistrano.rollback
    version: 2.0.1
userVoice