Skip to content

Instantly share code, notes, and snippets.

@johnkraczek
Last active September 24, 2025 02:22
Show Gist options
  • Select an option

  • Save johnkraczek/afc5f5416437ecf464468d864405ebc9 to your computer and use it in GitHub Desktop.

Select an option

Save johnkraczek/afc5f5416437ecf464468d864405ebc9 to your computer and use it in GitHub Desktop.
Bedrock Deploy to Shared Wordpress Hosting Inspired by Trellis

Trellis-Style Bedrock WordPress Deployment Guide for Shared Hosting

This guide provides step-by-step instructions for manually deploying a WordPress site using a Trellis-inspired symlink-based deployment strategy, which enables zero-downtime deployments and easy rollbacks onto shared hosting such as site ground or other hosts that provide composer and ssh access to your website.

Table of Contents

Prerequisites

Before starting, ensure you have:

  • SSH access to your remote server
  • Git repository with your WordPress project
  • Database credentials for your WordPress site
  • Composer installed on your remote server

Part 1: Provisioning

Provisioning is a one-time process that converts a traditional WordPress installation into a structure that supports Trellis-style deployments.

Step 1: Connect to your remote server and backup existing site

[Run on LOCAL]

# Set timestamp variable for consistent backup directory naming
TIMESTAMP=$(date +%Y%m%d%H%M%S)

# Create a local backup directory
mkdir -p ./backups/$TIMESTAMP

# Backup the entire WordPress installation
rsync -avz user@host:~/public_html/ ./backups/$TIMESTAMP/full_backup/

# Specifically backup critical files
rsync -avz user@host:~/public_html/wp-config.php ./backups/$TIMESTAMP/
rsync -avz user@host:~/public_html/.htaccess ./backups/$TIMESTAMP/

Step 2: Extract database credentials (for reference)

[Run on LOCAL]

# Extract and display DB credentials
grep "define.*DB_" ./backups/$TIMESTAMP/wp-config.php

Save these credentials, as you'll need them for the .env file during deployment.

Step 3: Create Trellis-style directory structure on remote server

[Run on REMOTE]

ssh user@host

# Create main deployment structure
mkdir -p ~/site/releases
mkdir -p ~/site/shared/uploads

Step 4: Preserve existing uploads directory content

[Run on REMOTE]

# Copy existing uploads to shared directory
if [ -d ~/public_html/wp-content/uploads ]; then
  cp -r ~/public_html/wp-content/uploads/* ~/site/shared/uploads/
  echo "Uploads preserved"
fi

Step 5: Remove traditional WordPress setup

[Run on REMOTE]

# Remove core WordPress files (careful with this command: Make sure you have a backup.)
rm -rf ~/public_html

Step 6: Create initial symlinks structure

[Run on REMOTE]

# Create the initial "current" symlink
# This will be updated with each deployment
mkdir -p ~/site/releases/initial/web
ln -sfn ~/site/releases/initial ~/site/current

# Create the public_html symlink that points to the current release's web directory
# This only needs to be done once during provisioning
ln -sf ~/site/current/web ~/public_html

Part 2: Deployment

Deployment is the process of pushing your Git repository to the server and making it live. This can be repeated for each update.

Step 1: Create a timestamped release directory

[Run on REMOTE]

ssh user@host

# Set timestamp for this release
TIMESTAMP=$(date +%Y%m%d%H%M%S)
mkdir -p ~/site/releases/$TIMESTAMP

Step 2: Clone your Git repository to the release directory

[Run on REMOTE]

# Clone repository to release directory
git clone [email protected]:your-org/site-repo.git ~/site/releases/$TIMESTAMP
cd ~/site/releases/$TIMESTAMP

Step 3: Copy .env file to the server

[Run on LOCAL]

# Copy the .env.production file from local to the remote server
scp .env.production user@host:~/site/releases/$TIMESTAMP/.env

[Run on REMOTE]

# Secure the .env file
chmod 600 ~/site/releases/$TIMESTAMP/.env

Step 4: Create symlinks to shared directories

[Run on REMOTE]

# Remove the uploads directory in the release and symlink to shared
rm -rf ~/site/releases/$TIMESTAMP/web/app/uploads
ln -sf ~/site/shared/uploads ~/site/releases/$TIMESTAMP/web/app/uploads

Step 5: Run Composer install (if needed)

[Run on REMOTE]

# Install Composer dependencies
cd ~/site/releases/$TIMESTAMP
composer install --no-dev --optimize-autoloader

Step 6: Update current symlink (atomic deployment)

[Run on REMOTE]

# Update the current symlink to point to the new release
# This is the key step that makes the deployment "atomic" and zero-downtime
ln -sfn ~/site/releases/$TIMESTAMP ~/site/current

Step 7: Clean up old releases (keep the last 5)

[Run on REMOTE]

# Remove old releases (keep last 5)
cd ~/site/releases
ls -1t | tail -n +6 | xargs -r rm -rf

Rollback Process

If you need to roll back to a previous release:

[Run on REMOTE]

# List available releases
ls -la ~/site/releases

# Update current symlink to point to the previous release
ln -sfn ~/site/releases/PREVIOUS_TIMESTAMP ~/site/current

# No need to update the public_html symlink as it always points to ~/site/current/web

Final Directory Structure

After completing both provisioning and deployment, your directory structure will look like:

~/
├── public_html -> ~/site/current/web (symlink)
└── site/
    ├── current -> ~/site/releases/20250920123456 (symlink to latest release)
    ├── releases/
    │   ├── 20250920123456/ (timestamped release directory)
    │   │   ├── .env
    │   │   ├── app/
    │   │   │   └── uploads -> ~/site/shared/uploads (symlink)
    │   │   ├── composer.json
    │   │   ├── web/
    │   │   └── ...
    │   ├── older_release_1/
    │   └── older_release_2/
    └── shared/
        └── uploads/ (persistent uploads directory)

This structure achieves:

  • Zero-downtime deployments - The public_html symlink is switched atomically
  • Rollback capability - Previous releases are preserved
  • Persistent data - Uploads and other shared data persists across deployments
  • Clean separation - Code and content are properly separated

Troubleshooting

Git clone issues

  • Verify SSH keys are set up correctly on the remote server
  • Test the git clone command manually to see specific errors

Database connection issues

  • Double-check the database credentials in your .env file
  • Ensure the database server allows connections from your hosting environment

Composer errors

  • Check if composer is installed and in the PATH
  • Try running composer with the --no-dev flag to skip development dependencies
  • Increase memory limit if needed: php -d memory_limit=-1 /path/to/composer install

Local WordPress Bedrock Development Setup Guide

This guide provides step-by-step instructions for setting up a Bedrock-based WordPress development environment using Local by Flywheel.

Table of Contents

Prerequisites

Before starting, ensure you have:

  • Local by Flywheel installed on your computer
  • Git installed and configured
  • Composer installed
  • GitHub account or other Git repository hosting service

Part 1: Initial Setup

Step 1: Create a new Local site

  1. Open Local by Flywheel
  2. Click "Create a new site"
  3. Follow the wizard to set up your site
  4. Remember your database credentials as you'll need them later

Step 2: Install Bedrock using Composer

[Run in Local's Terminal]

# Navigate to the parent directory of the public folder
cd ~/Local\ Sites/[site-name]/app

# Install Bedrock using Composer
composer create-project roots/bedrock

After this step, your directory structure should look like this:

~/Local Sites/[site-name]/
├── app/
│   ├── bedrock/        # The newly created Bedrock installation
│   └── public/         # The original Local public folder
├── conf/
│   ├── mysql/
│   ├── nginx/
│   └── php/
└── logs/
    ├── nginx/
    └── php/

Part 2: Directory Structure Configuration

Step 1: Back up the original public folder

[Run in Local's Terminal]

# Navigate to the app directory
cd ~/Local\ Sites/[site-name]/app

# Rename the public folder to public_old as a backup
mv public public_old

Step 2: Create a symlink to the Bedrock web directory

[Run in Local's Terminal]

# Create a symlink from Bedrock's web directory to the expected public folder
ln -s ~/Local\ Sites/[site-name]/app/bedrock/web ~/Local\ Sites/[site-name]/app/public

After these steps, your directory structure should look like this:

~/Local Sites/[site-name]/
├── app/
│   ├── bedrock/
│   │   ├── config/
│   │   ├── vendor/
│   │   ├── web/        # Bedrock's web directory
│   │   ├── .env
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── composer.json
│   │   └── ...
│   ├── public -> ~/Local Sites/[site-name]/app/bedrock/web    # Symlink to Bedrock's web directory
│   └── public_old/     # Backup of the original public folder
├── conf/
└── logs/

Step 3: Add your .env file

DB_NAME='local'
DB_USER='root'
DB_PASSWORD='root'

WP_HOME='https://yoursite.local'
WP_SITEURL="${WP_HOME}/wp"

Step 4: Verify your setup

  1. Open your site URL in a browser
  2. Ensure that WordPress loads correctly
  3. If you encounter any issues, check the troubleshooting section below

Part 3: Git Repository Setup

Step 1: Initialize a new Git repository

[Run in Local's Terminal]

# Navigate to the Bedrock directory
cd ~/Local\ Sites/[site-name]/app/bedrock

# Initialize a new Git repository
git init

# Add all files to the repository
git add .

# Make the initial commit
git commit -m "Initial site scaffold"

Step 2: Connect to a remote repository

[Run in Local's Terminal]

# Create a new repository on GitHub or your preferred Git hosting service first

# Add the remote repository
git remote add origin [email protected]:your-org/site-repo.git

# Push your local repository to the remote
git push -u origin master

Final Directory Structure

After completing all steps, your directory structure will look like this:

~/Local Sites/[site-name]/
├── app/
│   ├── bedrock/        # Git repository
│   │   ├── config/
│   │   ├── vendor/
│   │   ├── web/
│   │   │   ├── app/
│   │   │   ├── wp/
│   │   │   ├── index.php
│   │   │   └── wp-config.php
│   │   ├── .env
│   │   ├── composer.json
│   │   └── ...
│   ├── public -> ~/Local Sites/[site-name]/app/bedrock/web    # Symlink
│   ├── public_old/     # Backup of original public folder
│   └── sql/
│       └── local.sql
├── conf/
│   ├── mysql/
│   ├── nginx/
│   └── php/
└── logs/
    ├── mailpit/
    ├── mysql/
    ├── nginx/
    └── php/

This structure achieves:

  • Modern WordPress development - Using Bedrock's superior organization and dependency management
  • Git version control - Full repository setup for tracking changes
  • Local compatibility - Maintains Local by Flywheel's expected structure via symlinks
  • Easy deployment - Ready for deployment using the process in INSTRUCTIONS.md

Troubleshooting

WordPress doesn't load

  • Verify that the symlink was created correctly
  • Check Local's nginx and PHP logs for errors
  • Ensure the .env file in the Bedrock directory has the correct database credentials

Composer issues

  • Make sure you have the latest version of Composer installed
  • Try running composer with increased memory: php -d memory_limit=-1 /path/to/composer create-project roots/bedrock

Permission issues

  • Check that your user has sufficient permissions to create symlinks
  • On macOS/Linux: chmod -R 755 ~/Local\ Sites/[site-name]/app/bedrock/web

Git connection issues

  • Verify your SSH keys are set up correctly for GitHub
  • Test your GitHub connection: ssh -T [email protected]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment