Symfony and Dokku
Recently I’ve been using Dokku to deploy some of my side-projects, I wanted a simple deployment procedure that didn’t rely on complicated build systems, as these were just small apps that I needed to host somewhere easily. I’m using Dokku for that - so here’s some instructions for getting Symfony working with it.
I’ll preface this post by saying this blog post has sat in my drafts for a while. I had some troubles with getting this working, however it was due to the way I was running things in my Procfile
and since working out what was happening thanks to josegonzalez on GitHub - things are running just fine.
There’s not a lot to get a Symfony project up and running on Dokku. I’ve got a repository setup ready for it. The GitHub repository is here and if you’d like to see the app running, the demo site is here
Prerequisites
This is all assuming you have the DOKKU_CLIENT.SH
installed and setup locally - if not, you’ll need to add the appname into some of the configuration.
You’ll also need the dokku-mysql plugin installed.
Here’s the breakdown on how to get up and running:
Setup your Dokku app
You’ll need to make sure you’re inside your project folder, and that it’s a git repository (if you’ve already got a project that’s a git repository, skip this step)
> cd my-project
> git init
> git add .
> git commit -m 'Initial commit'
Then create a dokku app for your project:
> dokku apps:create symfony
You’ll get some output from the command about the app being created, and you can verify that it’s there by using:
> git remote -v
You should see a remote named dokku
that points to your Dokku server:
> git remote -v
dokku dokku@dokku.mfyu.co.uk:symfony (fetch)
dokku dokku@dokku.mfyu.co.uk:symfony (push)
Once the app is created, you’ll need to ensure that the correct environment (prod
) is setup for Symfony on Dokku, otherwise you’ll run into issues where Symfony will think it’s in development, when in reality it’s in production. This means that various classes can’t be found as they’ve not been installed (because they’ll be in your require-dev
section in composer.json
)
dokku config:set SYMFONY_ENV=prod
Create and configure MySQL
Next, if you need a database (which for my sample repo, you will) you’ll need to create that:
> dokku mysql:create symfony-db
Then, you’ll need to link that database to your app:
> dokku mysql:link symfony-db
Now, you’ll need to configure Doctrine within Symfony to look at that database
app/config.yml
# Doctrine Configuration
doctrine:
dbal:
driver: pdo_mysql
url: '%env(DATABASE_URL)%'
Note the use of the environment variable DATABASE_URL
- this is because when you link the MySQL database to your app, it exposes an environment variable that’s the dsn to connect to the database in the format: mysql://username:password@host-container:port/db-name
If you’re using a version of Symfony before 3.2 that doesn’t support using environment variables in config files, you can use environment variables from the Incenteev/ParameterHandler
package instead.
Nginx Configuration
You’ll need to configure nginx for Symfony’s re-write rules:
nginx.conf
location / {
# try to serve file directly, fallback to rewrite
try_files $uri @rewriteapp;
}
location @rewriteapp {
# rewrite all to app.php
rewrite ^(.*)$ /app.php/$1 last;
}
location ~ ^/app\.php(/|$) {
try_files @heroku-fcgi @heroku-fcgi;
internal;
}
And then setup a Procfile
to use that nginx config:
web: $(composer config bin-dir)/heroku-php-nginx -C nginx.conf web
Deployment tasks
If you wish to run tasks during the deployment process you can use an app.json
file in your project with predeploy
and postdeploy
tasks, note that as per the documentation: postdeploy changes are NOT committed to the app image.
- I forgot to read this and wondered why certain things weren’t working on another project, that’s why.
We have a predeploy
task of removing any files from the web/
directory we don’t want, such as app_dev.php
, app_test.php
and config.php
- you could always move this out into a separate script and call that if you find yourself needing to perform more tasks.
Then, we have a postdeploy
task that runs database migrations - now, this can cause some downtime if you’re running migrations on a large database, but if that’s the case, you’ll likely have a more complicated deployment setup with atomic deployments, ensuring that your app doesn’t have a maintenance window and the app is always available.
For this example, we’ll assume a minor bit of downtime is okay.
app.json
{
"scripts": {
"dokku": {
"predeploy": "rm -f web/app_*.php && rm -f web/config.php && php bin/console doctrine:migrations:migrate --allow-no-migration"
}
}
}
We also use --allow-no-migration
to ensure that doctrine doesn’t error when running migrations if there aren’t any.
UPDATE: I’ve moved this to the predeploy task, the reason being, that if a migration fails, we don’t want the deployment to continue.
Deploying
Once all that is done, it’s just a matter of deploying just as the dokku documentation tells us to:
> git push dokku master
And your app is live! Job done!