Let’s install Craft CMS 3 on macOS by using Lando to setup our local development environment. You can also follow these steps for Windows 10 Pro.
So what’s Lando?
Lando is a tool that provides a layer of abstraction on top of Docker. So you can start leveraging Docker now and postpone learning about Dockerfile and docker-compose
to network containers and setting up volumes and… 🤯
Lando comes from the Drupal community but you can use it to work locally with Laravel, WordPress, Django, Rails or even legacy PHP projects. In addition to running a wide range of web projects, it’s also actively maintained & has really good documentation.
Lando’s benefits:
- One click installer
- Cross-platform (macOS, Windows and Linux)
- Recipes with sensible defaults
- Per project config file
.lando.yml
which you commit to your Git repo to share your project’s tech stack! - easy to enable SSL / HTTPS
- uses faster & more lightweight Docker containers ‑vs- resource heavy virtual machines
Alright. Enough backstory. Let’s get started.
Go to Lando releases page & download most recent stable installer for your OS. Look for the green Latest release tag.
Run the Lando installer.
💥 IMPORTANT
If you already have Docker Desktop installed backup any databases you might need because Docker Desktop is packaged with Lando and it will overwrite your current installation.Adjust Docker Desktop settings.
💪 Increase resources for Docker
After Lando has finished installing. Open the Docker Desktop preferences, go to Resources and increase CPUs & Memory. I have a quad-core laptop with 16GB RAM. I allocated 4 CPUs, 6GB RAM, 4GB Swap and ~100GB to Disk Image.
⚠️ Disable Auto Update
Lando ships with its own version of Docker Desktop so if you update it past the version Lando expects you’ll run into issues.
In Preferences> General, uncheck Automatically check for updates.
- Install Craft CMS 3 via Lando CLI.
We’re going to use the Lando LAMP recipe. So Lando will configure Apache as our web server, PHP, MySQL and Composer.
a. Create a project directory mkdir craft3blog
b. cd into the new folder cd craft3blog
c. Now we’ll run lando init
This will start a wizard where we’ll specify our project’s tech stack.
Here’s the output of what I selected:
alexagui@alexMBP ~/Code/craft/craft3blog $ lando init
? From where should we get your app's codebase? current working directory
? What recipe do you want to use? lamp
? Where is your webroot relative to the init destination? web
? What do you want to call this app? craft3blog
NOW WE'RE COOKING WITH FIRE!!!
Your app has been initialized!
Go to the directory where your app was initialized and run `lando start` to get rolling.
Check the LOCATION printed below if you are unsure where to go.
Oh... and here are some vitals:
NAME craft3blog
LOCATION /Users/alexagui/Code/craft/craft3blog
RECIPE lamp
DOCS https://docs.devwithlando.io/tutorials/lamp.html
Before starting Lando let’s checkout the .lando.yml
file
name: craft3blog
recipe: lamp
config:
webroot: web
The recipe
line packs in a lot of power. It tells Lando spin up this project with most recent version of Apache, mysql & PHP. However, let’s say our production server uses PHP 7.2 & MariaDB 10.2
Then we can update it like so:
name: craft3blog
recipe: lamp
config:
webroot: web
php: '7.2'
database: mariadb:10.2
In fact, I updated mine to match the above.
d. Start Lando with lando start
. You’ll see output similar to this:
alexagui@alexMBP ~/Code/craft/craft3blog $ lando start
Let's get this party started! Starting app..
landoproxyhyperion5000gandalfedition_proxy_1 is up-to-date
Creating network "craft3blog_default" with the default driver
Creating volume "craft3blog_data_appserver" with default driver
Creating volume "craft3blog_home_appserver" with default driver
Creating volume "craft3blog_data_database" with default driver
Creating volume "craft3blog_home_database" with default driver
Creating craft3blog_appserver_1 ... done
Creating craft3blog_database_1 ... done
Waiting until appserver service is ready...
Waiting until database service is ready...
Waiting until database service is ready...
Waiting until database service is ready...
Waiting until database service is ready...
Waiting until database service is ready...
Waiting until database service is ready...
BOOMSHAKALAKA!!!
Your app has started up correctly.
Here are some vitals:
NAME craft3blog
LOCATION /Users/alexagui/Code/craft/craft3blog
SERVICES appserver, database
APPSERVER URLS https://localhost:32814
http://localhost:32815
http://craft3blog.lndo.site
https://craft3blog.lndo.site
Now let’s install Craft 3 by using the version of Composer installed by Lando.
lando composer create-project craftcms/craft boop
YES. I’m installing into a boop
folder. You’ll see why shortly.
After several screens of text you’ll see the boop
folder within our current folder.
alexagui@alexMBP ~/Code/craft/craft3blog $ ls
boop
But we actually need the contents of that boop
folder within our current folder.
So let’s move all those files up one level.
mv boop/* .
We also need to move some hidden dot . files.
mv boop/.* .
And with that done we can remove the boop
directory.
rm -rf boop
Q: Why didn’t I have Composer create the project within the current working directory and avoid the whole boop
folder shuffle? 🤔
A: Because Composer CANNOT install a project inside a directory that already contains files. In our case, .lando.yml
.
Before running the Craft installer we need to configure the the .env
file. If you open it, you’ll see something similar to this:
# The environment Craft is currently running in ('dev', 'staging', 'production', etc.)
ENVIRONMENT="dev"
# The secure key Craft will use for hashing and encrypting data
SECURITY_KEY=""
# The database driver that will be used ('mysql' or 'pgsql')
DB_DRIVER="mysql"
# The database server name or IP address (usually this is 'localhost' or '127.0.0.1')
DB_SERVER="localhost"
# The database username to connect with
DB_USER="root"
# The database password to connect with
DB_PASSWORD=""
# The name of the database to select
DB_DATABASE=""
# The database schema that will be used (PostgreSQL only)
DB_SCHEMA="public"
# The prefix that should be added to generated table names (only necessary if multiple things are sharing the same database)
DB_TABLE_PREFIX=""
# The port to connect to the database with. Will default to 5432 for PostgreSQL and 3306 for MySQL.
DB_PORT=""
We need to fill in the database credentials. And we can find them via the lando info
command.
alexagui@alexMBP ~/Code/craft/craft3blog $ lando info
[
{
service: 'appserver',
urls: [
'https://localhost:32814',
'http://localhost:32815',
'http://craft3blog.lndo.site',
'https://craft3blog.lndo.site'
],
type: 'php',
via: 'apache',
webroot: 'web',
config: {},
version: '7.2',
meUser: 'www-data',
hostnames: [
'appserver.craft3blog.internal'
]
},
{
service: 'database',
urls: [],
type: 'mariadb',
internal_connection: {
host: 'database',
port: '3306'
},
external_connection: {
host: 'localhost',
port: '32769'
},
creds: {
database: 'lamp',
password: 'lamp',
user: 'lamp'
},
config: {},
version: '10.2',
meUser: 'www-data',
hostnames: [
'database.craft3blog.internal'
]
}
]
I want to point out 2 important keys:
internal_connection> host
Notice the value is database. So in the Craft .env
file change it from localhost to DB_SERVER="database”
.
And if you look a bit further down you’ll see the creds
key. Those are the values that you need for database, username and password. Lando’s LAMP recipe by default assigns lamp
for those credentials.
However, I like to override these defaults with my own database credentials which makes it easier to manage multiple projects. If all my Lando databases are called lamp
it’ll be hard to keep track to which project a DB export named lamp_2019-10-07_11-10 AM.sql.gz
belongs to. It also makes it easier to configure my database client Sequel Pro for each project.
Open .lando.yml
and add the services
section like this:
name: craft3blog
recipe: lamp
config:
webroot: web
php: '7.2'
database: mariadb:10.2
services:
database:
type: mariadb:10.2
portforward: 3311
creds:
user: homestead
password: secret
database: craft3blog
Note that in addition to providing my own credentials, I’m also specifying an external port of 3311
. The reason for hardcoding an external port is because by default Lando will assign one randomly. Which means if I want to connect locally using Sequel Pro I have to first run lando info
to see which external port Lando assigned and update my Sequel Pro connection. I keep track of Lando project ports in a Notion doc.
Save .lando.yml
and run lando destroy
.
alexagui@alexMBP ~/Code/craft/craft3blog $ lando destroy
? Are you sure you want to DESTROY? Yes
Preparing to resign craft3blog to the dustbin of history...
Stopping craft3blog_appserver ... done
Stopping craft3blog_appserver_1 ... done
Stopping craft3blog_database_1 ... done
Removing craft3blog_appserver ... done
Removing craft3blog_appserver_1 ... done
Removing craft3blog_database_1 ... done
Removing network craft3blog_default
Removing volume craft3blog_data_appserver
Removing volume craft3blog_home_appserver
Removing volume craft3blog_data_database
Removing volume craft3blog_home_database
Your app has paid the IRON PRICE. App destroyed!
I ran lando destroy
because I’ve noticed that Lando containers sometimes are a bit finicky and will preserve config settings. By destroying it completely I’m ensuring our database overrides will take.
Start up Lando again.
lando start
And let’s confirm our database credentials have been overridden.
alexagui@alexMBP ~/Code/craft/craft3blog $ lando info
[
{
service: 'appserver',
urls: [
'https://localhost:32814',
'http://localhost:32815',
'http://craft3blog.lndo.site',
'https://craft3blog.lndo.site'
],
type: 'php',
via: 'apache',
webroot: 'web',
config: {},
version: '7.2',
meUser: 'www-data',
hostnames: [
'appserver.craft3blog.internal'
]
},
{
service: 'database',
urls: [],
type: 'mariadb',
internal_connection: {
host: 'database',
port: '3306'
},
external_connection: {
host: 'localhost',
port: '3311'
},
creds: {
database: 'craft3blog',
password: 'secret',
user: 'homestead'
},
config: {},
version: '10.2',
meUser: 'www-data',
hostnames: [
'database.craft3blog.internal'
]
}
]
I see our custom database credentials & external port. Looking good. Time to update the Craft .env
# The environment Craft is currently running in ('dev', 'staging', 'production', etc.)
ENVIRONMENT="dev"
# The secure key Craft will use for hashing and encrypting data
SECURITY_KEY="HLXcXNT3C98Ss2qg"
# The database driver that will be used ('mysql' or 'pgsql')
DB_DRIVER="mysql"
# The database server name or IP address (usually this is 'localhost' or '127.0.0.1')
DB_SERVER="database"
# The database username to connect with
DB_USER="homestead"
# The database password to connect with
DB_PASSWORD="secret"
# The name of the database to select
DB_DATABASE="craft3blog"
# The database schema that will be used (PostgreSQL only)
DB_SCHEMA="public"
# The prefix that should be added to generated table names (only necessary if multiple things are sharing the same database)
DB_TABLE_PREFIX=""
# The port to connect to the database with. Will default to 5432 for PostgreSQL and 3306 for MySQL.
DB_PORT="3306"
In addition to the credentials, I also entered the default MySQL database port of 3306
. Don’t confuse this with the custom external port we defined.
:sparkles: Craft 3.4 —> DB_DSN
Craft 3.4 deprecated the DB_DRIVER
, DB_SERVER
, DB_DATABASE
, and DB_PORT
keys. They replaced them with DB_DSN
. So instead of the above separate keys you’d have 1 line like this:
DB_DSN="mysql:host=database;port=3306;dbname=craft3blog"
You also need to fill in the SECURITY_KEY
. You can generate it using a password generator like this one.
We’re finally ready to install Craft. We can do so by visiting the following URL
http://craft3blog.lndo.site/admin/install
If all has gone well you should see a screen like this:
- Click the
Install Craft
button. - Accept the terms.
- Create your admin account.
Enter your admin user, email & password. - Setup your site info.
Enter the site name, base URL & language.
- Then wait a little bit as Craft finishes installing.
You’ll be redirected to the admin control panel Dashboard.
Welcome to Craft. 🎉
There isn’t any sample data or much of a frontend. That’s all left for you to customize as you see fit. 😉
When you’re done working with a Lando project just run:
lando stop
alexagui@alexMBP ~/Code/craft/craft3blog $ lando stop
This party's over :( Stopping app
Stopping craft3blog_appserver_1 ... done
Stopping craft3
And when you’re completely done working with Lando you can shutdown all apps with:
lando poweroff
alexagui@alexMBP ~/Code/craft/craft3blog $ lando poweroff
NO!! SHUT IT ALL DOWN! Spinning Lando containers down...
Bye bye landoproxyhyperion5000gandalfedition_proxy_1 ... done
Lando containers have been spun down.
So hopefully you’ve seen how convenient, flexible & powerful it is to use Lando for local development. One minor gripe is not being able to use a custom local URLs like eaglepeakweb.test
You must use appname.lndo.site
but that’s a small price to pay for all the other benefits.
If you have a fairly complex server config then a virtual machine or true Docker setup might be more appropriate.
Some advanced Lando techniques I recommend you try are:
- enable SSL / HTTPS
- configure some frontend tooling
- import a database
- connect to the local database using Sequel Pro
In the next article, we’ll start building a favorite board games library in Craft 3. 🎲