CraftCMS & Docksal: A Local Dev Match Made in Heaven
Tags: craftcms, platformsh, docker, setup
Published: 2021-04-02 15:25:02

If you've ever joined a new team you know the pain it can be to get a project successfully running locally on your machine. Without Docker there will be an untold number of non-documented configurations and it will inevitably take days to complete your setup. With docker, future updates can be complex and difficult with a myriad of options you don't need. 

Having recently found myself in this conundrum I decided to find ways to make local development easy to setup and easy to maintain. After a search across various solutions, Docksal was the clear winner. 

If you're not familiar with it, [Docksal](https://docs.docksal.io/getting-started/) allows you to simply and easily setup a dockerized, orchestrated environment without ever having to deal with docker compose. In addition, their CLI container comes pre-installed with many of the tools you will need to run common CMSs on popular hosting platforms.

In my case, I am using platform.sh for hosting. Platform.sh is a non-opinionated linux hosting solution that essentially gives you yaml-based configuration to spin up new environments and works seamlessly with git to spin up new environments for PRs and branches. Docksal has `platform`, Platform.sh's CLI, pre-installed so it works great out of the box. 

Docksal also has a CLI, `fin`, which is easily customizable to suit your needs. 

--- 

# Basic Setup

Our setup uses MySQL 5.7, Redis, & PHP 7.4. 

To get started using docksal create a `.docksal` directory at the root of your CraftCMS repo. Within it you'll need a `docksal.yml` and `docksal.env`.

Docksal comes standard with three containers: A database, a web server, and a CLI. You can specify which versions of PHP, MySQL, etc. within your docksal.env. To add new services, use your docksal.yml.


Here is a simple docksal.env. Any variable created here will be accessible within your `cli` container and within any custom commands you write. 

```bash
# .docksal/docksal.env

# Run `fin restart` anytime you change these values

PROJECT="project_name"

# Docksal Defaults
DOCKSAL_STACK=default
DOCROOT="web"
DB_IMAGE="docksal/mysql:5.7"
CLI_IMAGE="docksal/cli:php7.4"
COMPOSER_DEFAULT_VERSION=2

# Platform.sh
HOSTING_PLATFORM=platformsh
HOSTING_SITE=xxxxxxxxxx
HOSTING_ENV=production

# MySQL
MYSQL_DATABASE='db_name'
MYSQL_PORT_MAPPING='33061:3306'
MYSQL_ROOT_PASSWORD="root"
```

And here is a simple docksal.yml that brings in redis as a service. 

```yml
# .docksal/docksal.yml

version: "2.1"

services:
  redis:
    hostname: redis
    image: wodby/redis:4.0
    environment:
      - REDIS_MAXMEMORY=256m
```

In my case we use S3 for asset storage, so I'm leveraging the AWS CLI in my cli container. To pass the environment variables into the cli container I add this to my `docksal.yml` under `services` 

```yml
services:
  ...
  cli:
    environment:
      - AWS_ACCESS_KEY_ID
      - AWS_SECRET_ACCESS_KEY
```

You probably also have secrets you don't want committed to your repository because they are either sensitive or personalized. For that you can use a `docksal-local.env`. Add this to your `.gitignore` and use it just as you use the previous file. 

```bash
# .docksal/docksal-local.env

# Run `fin restart` anytime you change these values
SECRET_PLATFORMSH_CLI_TOKEN=""
DEV_USER=""
INTERNAL_API_KEY=""
DB_PW_STAGING=""
DB_PW_PROD=""
AWS_ACCESS_KEY_ID="" # note how these are used in the yml above
AWS_SECRET_ACCESS_KEY="" # note how these are used in the yml above
```

Once you have your configuration intact, go ahead and run `fin project start`. `fin` is the Docksal CLI and is quite powerful. 

If you are going my route of using AWS for asset storage, you'll also want to add a `.docksal/services/cli/startup.sh` script to install the AWS CLI. 

```bash
# .docksal/services/cli/startup.sh

sudo apt-get update
sudo apt-get install awscli -y
sudo apt-get install groff -y
aws configure set region us-east-1 --profile default
```

Anytime you make changes to your configuration files you can run `fin restart` to restart your containers. If you make changes to the images referenced you may need to run `fin project reset <container>` e.g. `fin project reset cli`. 

## Custom Commands

This is all fine and dandy, but the real magic comes in the form of custom commands. We use these ALL THE TIME. 

Go ahead and create a `commands` directory under `.docksal`. Any file you place in this directory will be a new command that `fin` can run. 

You can pretty much use any language to write these commands, but I have stuck with bash for now. 

One example could be to have an `init` command that gets docksal running, then installs composer and npm dependencies within the CLI container. Note that these commands will not be dependent on your local npm/composer installs but on what exists in the cli container.

```bash
# .docksal/commands/init

#!/bin/sh

BLUE=\\x1B[34m
YELLOW=\\x1B[33m
GREEN=\\x1B[32m
RED=\\x1B[31m
RESET=\\x1b[0m

## Removes any previous containers this preject may have created, then starts and executes npm install/composer install
## Usage: fin init

# Forces whole job to error on failure
set -e

echo "${YELLOW}REMOVING ANY OLD CONTAINERS${RESET}"
fin project rm -f

echo "${YELLOW}STARTING PROJECT CONTAINERS${RESET}"
fin project start

echo "${YELLOW}RUNNING NPM INSTALL${RESET}"
fin exec npm install

echo "${YELLOW}RUNNING COMPOSER INSTALL${RESET}"
fin exec composer install
```

The most useful of these commands is probably the [`setup` command which allows new devs to get up and running within minutes](https://gist.github.com/mattbloomfield/08ae5d8bda27672b3205b2ad3df5d085). It leverages a lot of the jobs below.

Other custom commands I have written and use often: 
* [Sync Database & Files from Remote (with local asset strategy)](https://gist.github.com/mattbloomfield/92103e409a5d6f6ea8105be641d3ae6b)
 * [Dependent on this sync_files job](https://gist.github.com/mattbloomfield/95cd9e65382f98a9412d93b81df0b318)
* [Sync Database & Files from Remote (with AWS S3 asset strategy](https://gist.github.com/mattbloomfield/e20c86edd61a745723343e123667f3a5)
* [Sync Database & Files between Remotes (with local asset strategy](https://gist.github.com/mattbloomfield/91dad103676742833575fb887617e5a3)
 * [Dependent on this push_files job](https://gist.github.com/mattbloomfield/271112ae2de1dcdb5585e5fd14d55f94)
* [Sync Database & Files between Remotes (with AWS S3 asset strategy)](https://gist.github.com/mattbloomfield/4a1ab88a13bdf98860753caf1a0500b5)
* [Livestream Logs](https://gist.github.com/mattbloomfield/a1c65b525633916ba51c5c82bd599b9a)
* [Replicate a Deploy Locally](https://gist.github.com/mattbloomfield/5ad494843f647b3dfa6582a222db8dda)
* [SSH into Platform Container](https://gist.github.com/mattbloomfield/7d8f61e4f40f4e9d1ec065d2a8a9bc14)
* [Dump Local Database](https://gist.github.com/mattbloomfield/cf80a3e3487651e04dd7305ca8e8562d)
* [Activate Custom Git Hooks](https://gist.github.com/mattbloomfield/ac893e54ba897a643ff2ecd11468c6ce)

We use a lambda function to store our local .env file for CraftCMS since it can securely hold secrets. We then request it using a custom command and secure key - basically minimizing the number of tokens we are passing around in Slack/Email. This custom command is found here: 

* [Refresh Local .env](https://gist.github.com/mattbloomfield/1fd8ad143e5b5f394ac7fe061cf5f74d)

And the [node.js-powered Lambda Function is here](https://gist.github.com/mattbloomfield/b037cad5b341c8cd51e4bacb20f9ef1a). 

## Accessing the CLI container

Sometimes you need to ssh into the CLI container to run certain things, e.g. craft commands like `php craft db/migrate`

This can be done using the `fin bash` command. 

## Platform.sh Token confusion. 

It's always best to run platform commands within the CLI container. This is done by prepending the command with `fin`. This is because the CLI container comes pre-installed with the `platform` CLI. If it prompts you to log in, [generate an API token](https://docs.platform.sh/development/cli/api-tokens.html) and store it in your `.docksal/docksal-local.env`

---

And there you have it! You're all ready to start developing on CraftCMS using Docksal to supercharge your local setup. 

If you like this approach let me know in the comments! If you've found something better, let me know that as well.