For years I’ve been using wildcard subdomains for local development rather than having to set up virtual hosts every time I want to spin up a new site. I was using a combination of nginx and dnsmasq to bring it all together. I’d use dnsmaq to make all calls to any subdomain on my development URL point to 127.0.0.1. I’ve been using
*.lee.test
as my local development domain. So, if I wanted to spin up a new WordPress site for a new project I’d use the domain project-name.lee.test
. Then I’d set nginx to use the subdomain as the web root directory for the project. So, I’d have a directory like /home/lee/sites/project-name
.
This was all great until Ubuntu 16.10 came around and stopped using dnsmaq in favor of systemd-resolved
. The dnsmasq packages are still available for Ubuntu 16.10, 17.04, and 17.10, but I really don’t want to have both dnsmaq AND systemd-resolver running at the same time. I also don’t want to replace systemd-resolver with dnsmasq. So… I have modified my workflow a bit with this bash script that writes domains directly to /etc/hosts
. So, here’s how I do things now.
The Tools
Here’s a quick explanation of my local development setup.
First, I have created a directory called /home/lee/sites
where I put all my web development projects / WordPress sites.
PHP: phpbrew
I’m using phpbrew fpm so I can flip around between various version of PHP. I do a lot of WordPress development and the official recommendation from WordPress.org is to use PHP 7, but a ton of WordPress sites are still running PHP 5.6 (and sometimes even older than that). So, I need to be able to flip things around so my local version of PHP matches the live site where I’ll be deploying the project. So, I use phpbrew and it’s excellent.
Web server: nginx
I’m using nginx as my web server and have it set up to pluck off the subdomain and use it for the root directory name for the project. Here’s what my sites-enabled/default
nginx config file looks like.
server { listen 80 default_server; listen [::]:80 default_server; # Use the subdomain as document root server_name ~^(?.+)\.lee\.local$; root /home/lee/sites/$vhost; # Add index.php to the list if you are using PHP index index.php index.html index.htm index.nginx-debian.html; server_name _; location / { # First attempt to serve request as file, then # as directory, then append the query to index.php try_files $uri $uri/ /index.php?q=$uri&$args; } # pass PHP scripts to FastCGI server location ~ \.php$ { include snippets/fastcgi-php.conf; # Get PHP working with phpbrew php-fpm fastcgi_pass 127.0.0.1:9000; } }
WP-CLI
I use WP-CLI to quickly spin up WordPress sites for local development. To make things quick and efficient, I’ve set up a config.yml
file with some default settings for things like the database credentials, the WordPress admin user credentials, and a few tweaks to the wp-config.php
file to enable debugging and to disable storing post revisions.
The config file lives in ~/.wp-cli/config.yml
and looks like this.
core install: admin_user: dev-username admin_password: dev-password admin_email: your@email.com title: WordPress Development core config: dbuser: db-username dbpass: db-password dbhost: localhost extra-php: | define( 'WP_DEBUG', true ); define( 'WP_POST_REVISIONS', 0 );
Let me stress a the point that this is only for local development and not for a live, production server. You obviously need to be much more careful regarding your WordPress and database credentials when publishing live/production sites.
Spinning Up Sites
Here’s where it all comes together and gets awesome. With a single command I can spin up a fresh WordPress site in just a couple seconds. I wrote a little bash script that:
- Downloads the latest version of WordPress from WordPress.org
- Configures WordPress with my development info
- Creates the database for the WordPress site
- Installs WordPress on the domain I specify
- Adds the domain to
/etc/hosts
Here’s the script:
#!/bin/bash # Usage: # >wp-site.sh $subdomain [$port] mkdir ~/sites/$1 cd ~/sites/$1 wp core download wp core config --dbname=$1 wp db create if [ $2 ] then wp core install --url=$1.lee.test:$2 else wp core install --url=$1.lee.test fi # Add domain to /etc/hosts echo -e "127.0.0.1\t$1.lee.test" | sudo tee -a /etc/hosts > /dev/null
The last line of the script is the new change now that I’m no longer using dnsmasq. That last line works like this:
echo -e
evaluate the \t as a tab"127.0.0.1\t$1.lee.test"
use the 1st argument passed to the script as the subdomain for the website. The string needs to be in double quotes to evaluate the $1. Otherwise it will just print a literal$1
in the/etc/hosts
file which is not what we want.| sudo tee -a /etc/hosts
pipe the line we want to add to/etc/hosts
to thetee
command withsudo
privileges because/etc/hosts
is owned byroot
. Make sure to use-a
to append the line to/etc/hosts
, otherwise you’ll overwrite all the contents of the file which would be bad.> /dev/null
suppress the output of thetee
command so we don’t have to see the output in the terminal.
I chose to use the tee
command rather that applygin sudo
to the entire echo
statement so that the sudo
permissions are applied to as little of the command as possible and the only part of the command that actually needs sudo
privileges.
Example Usage
To see it all in action, suppose we wanted to spin up a site to work on cart66. This one command does the whole thing in about 3 seconds.
> wp-site.sh cart66
Downloading WordPress 4.8.2 (en_US)...
Using cached file '/home/lee/.wp-cli/cache/core/wordpress-4.8.2-en_US.tar.gz'...
Success: WordPress downloaded.
Success: Generated 'wp-config.php' file.
Success: Database created.
Success: WordPress installed successfully.
If you happen to see a line like this sh: 1: -t: not found
in your output, don’t worry. It just means your php.ini
does not have any value set for the sendmail_path
. You can either ignore the line or edit your php.ini
file and include the line sendmail_path = sendmail -t -i
(which is the default value – you might want something different).