Shopware 6 Production and Staging Environment (2/2)

Complete Setup and Automated Deployment Guide

Table of Contents

This guide is divided into two parts:

  • Part 1: Server Environment Setup (Production & Staging) - Basic Infrastructure
  • Part 2: Development Environment and Automated Deployment - CI/CD Pipeline

Part 2: Development Environment and Automated Deployment

In Part 1, we built the Shopware6 production and staging environment on the server. Part 2 covers everything from local development environment setup to implementing automated deployment. The following steps show how to build an efficient development environment and implement automated deployment with GitHub Actions.

1. Prepare Local Development Environment

Why a Good Development Environment Matters

A well-configured development environment should give you:

  • Production Parity: Same PHP and Node.js versions as your servers
  • Quick Setup: Get up and running with minimal effort using Dockware
  • Debugging Power: Step-through debugging and profiling with Xdebug
  • Fast Feedback: Instant file change detection and reloading

Let’s get your local Shopware6 development environment ready.

Verify Docker Installation

First, make sure Docker is installed and running:

docker --version
docker-compose --version

Create Project Directory

mkdir shopware-project
cd shopware-project

2. Shopware6 Setup with Dockware

Create Initial docker-compose.yml

nano docker-compose.yml

Enter the following content:

services:
  shopware:
    image: dockware/dev:latest # Specify same version as production environment
    container_name: shopware # Container name
    ports:
      - "80:80"
      - "3306:3306"
      - "22:22"
      - "8888:8888"
      - "9999:9999"
      - "9998:9998"
    networks:
      - web
    environment:
      - XDEBUG_ENABLED=1
      - PHP_VERSION=8.3 # Same version as production environment
      - NODE_VERSION=22 # Same version as production environment

networks:
  web:
    external: false

Shopware6 Initial Installation

# Start container
docker compose up -d

# Check container start
docker ps

# Copy Shopware files to host
mkdir -p ./src
docker cp shopware:/var/www/html/. ./src

# Set permissions
docker exec -it shopware bash -c 'sudo chown www-data:www-data /var/www/html -R'

# Stop container once
docker compose down

Update docker-compose.yml

Replace with the following content:

services:
  shopware:
    image: dockware/dev:latest
    container_name: shopware
    volumes:
      - "./src:/var/www/html/"
      # Exclusion volumes (managed in container)
      - "/var/www/html/.git/"
      - "/var/www/html/public/build"
      - "/var/www/html/var/cache"
      - "/var/www/html/vendor"
      - "db_volume:/var/lib/mysql"
    ports:
      - "80:80"
      - "3306:3306"
      - "22:22"
      - "8888:8888"
      - "9999:9999"
      - "9998:9998"
    networks:
      - web
    environment:
      - XDEBUG_ENABLED=1
      - PHP_VERSION=8.3
      - NODE_VERSION=22

volumes:
  db_volume:
    driver: local

networks:
  web:
    external: false

Start and Check Development Environment

# Restart container
docker compose up -d

# Check in browser
echo "Shop: http://localhost"
echo "Admin: http://localhost/admin (admin/shopware)"

3. Git Management Configuration for Project

Create .gitignore File

Enter the following content:

# IDE
/.idea
/.vscode

# Dependencies
/vendor/
/node_modules/

# Environment files
.env.local
.env.local.php
.env.*.local

# Generated/Compiled Assets
/public/bundles/
/public/css/
/public/fonts/
/public/js/
/public/theme/
/public/asset-manifest.json

# Cache & Temporary Files
/public/thumbnail/*
/public/sitemap/*
/public/media/cache/
/public/media/temp/
/var/cache/
/var/log/
/var/session/
/files/cache/
/files/temp/
/files/media_tmp/

# Security & Installation
/auth.json
/install.lock
/config/jwt/

# Shopware export files
/files/export/

# Docker
docker-compose.override.yml

# Keep important directories
!/var/.htaccess
!/files/.htaccess

# Custom plugins managed by Composer
/custom/plugins/*
!/custom/plugins/.gitkeep

4. Add Necessary Dependencies

Add Deployer-related packages in container:

# Enter container
docker exec -it shopware bash

# Install Deployer-related packages
composer require deployer/deployer shopware/deployment-helper

# Exit container
exit

5. Deployer Configuration

Create deploy.php File

nano deploy.php

Enter the following content (change server IP address accordingly):

<?php

namespace Deployer;

require_once 'recipe/common.php';
require_once 'contrib/cachetool.php';

set('bin/console', '{{bin/php}} {{release_or_current_path}}/bin/console');
set('cachetool', '/run/php/php8.3-fpm.sock');
set('application', 'Shopware 6');
set('allow_anonymous_stats', false);
set('default_timeout', 3600);

// Staging environment configuration
host('staging')
    ->setHostname('YOUR_SERVER_IP') // Change to server IP address
    ->setLabels([
        'type' => 'web',
        'env'  => 'staging',
    ])
    ->setRemoteUser('www-data')
    ->set('deploy_path', '/var/www/shopware-staging')
    ->set('http_user', 'www-data')
    ->set('writable_mode', 'chmod')
    ->set('keep_releases', 3);

// Production environment configuration
host('production')
    ->setHostname('YOUR_SERVER_IP') // Change to server IP address
    ->setLabels([
        'type' => 'web',
        'env'  => 'production',
    ])
    ->setRemoteUser('www-data')
    ->set('deploy_path', '/var/www/shopware-prod')
    ->set('http_user', 'www-data')
    ->set('writable_mode', 'chmod')
    ->set('keep_releases', 3);

// Shared files
set('shared_files', [
    '.env.local',
    'install.lock',
    'public/.htaccess',
    'public/.user.ini',
]);

// Shared directories
set('shared_dirs', [
    'config/jwt',
    'files',
    'var/log',
    'public/media',
    'public/thumbnail',
    'public/sitemap',
    'custom/plugins',
    'custom/apps',
]);

// Writable directories
set('writable_dirs', [
    'config/jwt',
    'files',
    'public/bundles',
    'public/css',
    'public/fonts',
    'public/js',
    'public/media',
    'public/sitemap',
    'public/theme',
    'public/thumbnail',
    'var',
]);

// Shopware-specific tasks
task('sw:deployment:helper', static function() {
    run('cd {{release_path}} && vendor/bin/shopware-deployment-helper run');
});

task('sw:assets:install', static function() {
    run('cd {{release_path}} && {{bin/console}} assets:install');
});

task('sw:touch_install_lock', static function () {
    run('cd {{release_path}} && touch install.lock');
});

task('sw:health_checks', static function () {
    run('cd {{release_path}} && bin/console system:check --context=pre_rollout');
});

// Upload task
task('deploy:update_code')->setCallback(static function () {
    upload('.', '{{release_path}}', [
        'options' => [
            '--exclude=.git',
            '--exclude=deploy.php',
            '--exclude=node_modules',
            '--exclude=.github',
        ],
    ]);
});

// Main deployment task
desc('Deploys your project');
task('deploy', [
    'deploy:prepare',
    'deploy:clear_paths',
    'sw:deployment:helper',
    'sw:assets:install',
    'sw:touch_install_lock',
    'deploy:publish',
]);

// Hooks
after('deploy:failed', 'deploy:unlock');
after('deploy:symlink', 'cachetool:clear:opcache');
after('sw:touch_install_lock', function () {
    if (get('labels')['env'] === 'production') {
        invoke('sw:health_checks');
    }
});

6. Generate and Configure SSH Keys

Generate SSH Keys Locally

# Generate SSH key
ssh-keygen -t ed25519 -f ~/.ssh/shopware_deploy

# Copy public key
cat ~/.ssh/shopware_deploy.pub

Add Public Key to Server

Execute the following on server:

# Add public key to authorized_keys (YOUR_SSH_PUBLIC_KEY_CONTENT is the content copied above)
sudo -u www-data tee -a /var/www/.ssh/authorized_keys << 'EOF'
YOUR_SSH_PUBLIC_KEY_CONTENT_HERE
EOF

# Set permissions
sudo chmod 600 /var/www/.ssh/authorized_keys

SSH Connection Test

# Connection test from local
ssh -i ~/.ssh/shopware_deploy www-data@YOUR_SERVER_IP

7. GitHub Actions Workflow Configuration

Smart Branching Strategy

  • staging: Every push automatically deploys to staging for testing
  • main: Production deployments (only merge after staging verification!)

Create .github/workflows Directory

mkdir -p .github/workflows

Workflow for Staging Environment

nano .github/workflows/deploy-staging.yml

Enter the following content:

name: Deploy to Staging
on:
  push:
    branches:
      - staging

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.3"

      - name: Install Shopware CLI
        uses: shopware/shopware-cli-action@v1

      - name: Build
        run: shopware-cli project ci .

      - name: Deploy
        uses: deployphp/action@v1
        with:
          dep: deploy staging
          private-key: ${{ secrets.SSH_PRIVATE_KEY }}

Explanation of GitHub Actions Steps

Role of each step:

  • Checkout: Download repository code to runner
  • Setup PHP: Prepare same PHP 8.3 as production environment
  • Install Shopware CLI: Install Shopware6-specific build tool
  • Build: Asset build and optimization (shopware-cli project ci command)
  • Deploy: Deployment to server with Deployer

Workflow for Production Environment

nano .github/workflows/deploy-production.yml

Enter the following content:

name: Deploy to Production
on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: "8.3"

      - name: Install Shopware CLI
        uses: shopware/shopware-cli-action@v1

      - name: Build
        run: shopware-cli project ci .

      - name: Deploy
        uses: deployphp/action@v1
        with:
          dep: deploy production
          private-key: ${{ secrets.SSH_PRIVATE_KEY }}

8. Create and Configure GitHub Repository

Initialize Repository

# Initialize Git repository
git init

# Create .gitkeep file
touch custom/plugins/.gitkeep

# First commit
git add .
git commit -m "Initial commit"

# Add remote after creating GitHub repository
git remote add origin https://github.com/your-username/your-shopware-project.git
git branch -M main
git push -u origin main

# Create staging branch
git switch -c staging
git push -u origin staging

Configure GitHub Secrets

Copy Private Key
# Copy private key locally
cat ~/.ssh/shopware_deploy
Add Secret in GitHub Repository Settings
  1. Open repository page Settings > Secrets and variables > Actions
  2. Click New repository secret
  3. Add as follows:
  • Name: SSH_PRIVATE_KEY
  • Value: Paste copied private key (from -----BEGIN OPENSSH PRIVATE KEY----- to -----END OPENSSH PRIVATE KEY-----)

9. Custom Plugin Management

Benefits of VCS Management

By managing custom plugins in separate repositories:

  • Independent Development: Plugin and main project independent
  • Version Management: Individual versioning of plugins
  • Reusability: Same plugins usable in other projects
  • Team Development: Easy collaboration with plugin development team

When managing custom plugins in separate repositories:

Update composer.json

Add the following to composer.json:

{
  "require": {
    "your-org/your-plugin": "dev-develop"
  },
  "repositories": [
    {
      "type": "vcs",
      "url": "https://github.com/your-org/your-plugin.git"
    }
  ]
}

Environment-specific Configuration Examples

Staging Environment (for development and testing):

"your-org/your-plugin": "dev-develop"

Production Environment (only stable versions):

"your-org/your-plugin": "^1.0.0"

Configure GitHub Access Token

A access token is required to access GitHub repositories. Generate token on GitHub and configure in Shopware container.

# Execute in container
docker exec -it shopware bash
composer config --global github-oauth.github.com YOUR_GITHUB_TOKEN
exit

Server Configuration (PHP-FPM Settings)

Add the following configuration to /etc/php/8.3/fpm/pool.d/www.conf. Without this configuration, errors occur during Shopware updates:

env[PATH] = /usr/local/bin:/usr/bin:/bin
env[GIT_EXEC_PATH] = /usr/bin
env[COMPOSER_AUTH] = '{"github-oauth":{"github.com":"YOUR_ACTUAL_TOKEN_HERE"}}'

After configuration, restart PHP-FPM:

sudo systemctl restart php8.3-fpm

10. Execute First Deployment

Check Deployment Flow

First deployment flow:

  1. Staging: Push to staging branch and function check
  2. Production Preparation: After confirmation in staging, merge to main branch
  3. Production Deployment: Push to main branch for automated deployment

Deployment to Staging Environment

# Switch to staging branch
git switch staging

# Push changes (GitHub Actions will run automatically)
git push origin staging

Activate Staging Mode

After deployment completion, execute the following on server:

# Switch to staging directory
cd /var/www/shopware-staging/current

# Activate staging mode
bin/console system:setup:staging

# Clear cache
bin/console cache:clear

exit

Effect of Staging Mode:

  • Prevents search engine indexing
  • Adds “Staging” display to admin panel
  • Clear distinction from production environment

Deployment to Production Environment

# Switch to main branch
git switch main

# Merge changes from staging
git merge staging

# Push (GitHub Actions will run automatically)
git push origin main

Summary

Benefits of the Built System

Through these steps, a Shopware6 production and staging environment as well as automated deployment system with the following benefits can be built:

  1. Security: Firewall configuration, SSH key authentication, environment separation
  2. Performance: PHP-FPM, Caddy optimization, OPcache utilization
  3. Operability: Zero-downtime deployment with Deployer
  4. Maintainability: Environment-specific configuration, version control with Git

I hope this article helps in building the Shopware6 production environment. If there are improvement points in the article or you have questions or feedback, please feel free to contact me!


References