Shopware 6 Production and Staging Environment (1/2)

Complete Setup and Automated Deployment Guide

Table of Contents

While researching how to set up a production environment for Shopware6, I was surprised to find very little well-organized information available. This guide documents my hands-on experience building both production and staging environments for Shopware6, complete with automated deployment using GitHub Actions.

This comprehensive tutorial walks you through every step—from initial server setup to fully automated deployment. By the end, you’ll have a robust, production-ready Shopware6 environment up and running.

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

Technical Overview

Here’s the technology stack we’ll be working with:

  • OS: Ubuntu 24.04 LTS (rock-solid stability with long-term support)
  • Web Server: Caddy (automatic HTTPS certificates and simple configuration)
  • PHP: 8.3 (meets the latest Shopware6 requirements)
  • Database: MySQL 8.0 (Shopware6’s recommended database)
  • Deployment Tool: Deployer (purpose-built for PHP applications)
  • CI/CD: GitHub Actions (powerful and free)
  • Development Environment: Dockware (Docker container optimized for Shopware6)

This stack delivers both production-grade stability and streamlined deployment workflows.

Part 1: Server Environment Setup (Production & Staging)

In Part 1, we’ll build the foundation for both production and staging environments. This section covers everything from initial server setup to security configuration and software installation.

Prerequisites

Before we begin, make sure you have:

  • Server: Ubuntu 24.04 LTS (VPS or cloud instance)
  • Access: Root or sudo privileges
  • Domain: Purchased domain with DNS configured
  • Knowledge: Basic familiarity with Linux commands

1. Basic Security Settings

Setting Up the Firewall

First, let’s secure our server by configuring the firewall. Shopware6 needs HTTP (80) and HTTPS (443) ports open for web traffic.

# Check current status
sudo ufw status verbose

# Open HTTP/HTTPS ports
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable firewall
sudo ufw enable

# Verify settings
sudo ufw status verbose

2. PHP 8.3 Installation and Configuration

Installing PHP 8.3

Let’s install PHP 8.3 with all the extensions Shopware6 needs:

# Add PPA repository (Ondřej Surý's high-quality PHP packages)
sudo add-apt-repository ppa:ondrej/php
sudo apt-get update

# Install PHP 8.3 and required extensions
sudo apt-get install -y php8.3-fpm php8.3-mysql php8.3-curl php8.3-gd php8.3-xml php8.3-zip php8.3-opcache php8.3-mbstring php8.3-intl php8.3-cli

Optimizing PHP Configuration

Shopware6 is quite memory-hungry, especially with lots of products or complex plugins. Let’s boost the default settings to prevent issues.

Edit /etc/php/8.3/fpm/php.ini and update these settings:

sudo nano /etc/php/8.3/fpm/php.ini

Change the following settings:

memory_limit = 512M           ; Shopware6 recommended value (default is 128M)
max_execution_time = 30       ; Script execution time limit
upload_max_filesize = 20M     ; Upload file size limit
post_max_size = 20M           ; POST data size limit

PHP-FPM Pool Configuration

Adjust PHP-FPM pool settings. Edit /etc/php/8.3/fpm/pool.d/www.conf:

sudo nano /etc/php/8.3/fpm/pool.d/www.conf

Check/change the following settings:

listen = /run/php/php8.3-fpm.sock
user = www-data
group = www-data
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

This configuration enables communication between Caddy and PHP-FPM via Unix sockets, which is faster than TCP.

3. Composer Installation

Install PHP dependency management tool Composer:

sudo wget https://getcomposer.org/download/latest-stable/composer.phar -O /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer

# Verify installation
composer --version

4. Node.js Installation

Install Node.js 22:

curl -fsSL https://deb.nodesource.com/setup_22.x -o nodesource_setup.sh
sudo -E bash nodesource_setup.sh
sudo apt-get install -y nodejs

# Verify installation
node --version
npm --version

5. Caddy Webserver Installation

Now let’s install Caddy, which will automatically handle HTTPS certificates for us:

# Install required packages
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl

# Add Caddy GPG key
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg

# Add Caddy repository
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list

# Install Caddy
sudo apt update
sudo apt install caddy

6. Caddy Configuration

Edit /etc/caddy/Caddyfile and configure for Shopware6:

sudo nano /etc/caddy/Caddyfile

Replace with the following content (change domain names accordingly):

# Production environment configuration
yourdomain.com www.yourdomain.com {
  # Security headers
  header {
    X-Frame-Options DENY
    Referrer-Policy no-referrer-when-downgrade
  }

  # Security settings for SVG files
  @svg {
    file
    path *.svg
  }
  header @svg Content-Security-Policy "script-src 'none'"

  # Matcher to exclude static files
  @default {
    not path /theme/* /media/* /thumbnail/* /bundles/* /css/* /fonts/* /js/* /recovery/* /sitemap/*
  }

  # Document Root
  root * /var/www/shopware-prod/current/public
  php_fastcgi unix//run/php/php8.3-fpm.sock
  encode zstd gzip
  file_server
}

# Staging environment configuration
staging.yourdomain.com {
  # Security headers
  header {
    X-Frame-Options DENY
    Referrer-Policy no-referrer-when-downgrade
  }

  # Security settings for SVG files
  @svg {
    file
    path *.svg
  }
  header @svg Content-Security-Policy "script-src 'none'"

  # Matcher to exclude static files
  @default {
    not path /theme/* /media/* /thumbnail/* /bundles/* /css/* /fonts/* /js/* /recovery/* /sitemap/*
  }

  # Document Root
  root * /var/www/shopware-staging/current/public
  php_fastcgi unix//run/php/php8.3-fpm.sock
  encode zstd gzip
  file_server
}

7. MySQL 8.0 Installation and Configuration

Time to set up our database server:

sudo apt update
sudo apt install -y mysql-server

# Run security configuration
sudo mysql_secure_installation

During the security setup, it’s generally safe to answer “yes” to all questions.

Create Database and User

Log into MySQL and create database and user for Shopware:

sudo mysql -u root -p

Execute the following SQL (change password accordingly):

-- Create databases
CREATE DATABASE shopware CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE shopware_staging CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Create user and grant permissions
CREATE USER 'shopware'@'localhost' IDENTIFIED BY 'your_secure_password';
GRANT ALL PRIVILEGES ON shopware.* TO 'shopware'@'localhost';
GRANT ALL PRIVILEGES ON shopware_staging.* TO 'shopware'@'localhost';

-- Set packet size
SET GLOBAL max_allowed_packet = 33554432;
FLUSH PRIVILEGES;
EXIT;

Optimize MySQL Configuration File

Optimize MySQL performance. Edit /etc/mysql/mysql.conf.d/mysqld.cnf:

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Add the following settings:

max_allowed_packet = 32M              # Support for large data processing
innodb_buffer_pool_size = 1G          # Memory cache size (70-80% of server memory recommended)
innodb_log_file_size = 256M           # Transaction log size

Restart MySQL to apply settings:

sudo systemctl restart mysql

8. Create Deployment Directory Structure

Deployer Directory Structure Design Philosophy

Deployer realizes zero-downtime deployment with the following structure:

/var/www/shopware-prod/
├── current -> releases/3                # Symbolic link to current version
├── releases/                            # Stores each release
│   ├── 1/                               # First release
│   ├── 2/                               # Second release
│   └── 3/                               # Latest release (currently active)
└── shared/                              # Data maintained between deployments
    ├── .env.local                       # Environment configuration
    ├── files/                           # Upload files
    ├── var/log/                         # Log files
    ├── config/jwt/                      # JWT certificates
    └── public/media/                    # Product images etc.

This structure enables near-zero downtime since the symbolic link is only switched after the new version deployment is complete.

Create directory structure for Deployer:

# Production environment
sudo mkdir -p /var/www/shopware-prod/{releases,shared}
sudo mkdir -p /var/www/shopware-prod/shared/{files,var/log,config/jwt}
sudo mkdir -p /var/www/shopware-prod/shared/public/{media,thumbnail,sitemap}

# Staging environment
sudo mkdir -p /var/www/shopware-staging/{releases,shared}
sudo mkdir -p /var/www/shopware-staging/shared/{files,var/log,config/jwt}
sudo mkdir -p /var/www/shopware-staging/shared/public/{media,thumbnail,sitemap}

# Transfer ownership to www-data
sudo chown -R www-data:www-data /var/www/shopware-prod
sudo chown -R www-data:www-data /var/www/shopware-staging

# Set appropriate permissions
sudo chmod -R 755 /var/www/shopware-prod
sudo chmod -R 775 /var/www/shopware-prod/shared
sudo chmod -R 755 /var/www/shopware-staging
sudo chmod -R 775 /var/www/shopware-staging/shared

Importance of Permission Settings

Reasons for permission settings:

  • 755: For general directories and files (readable/executable, only owner can write)
  • 775: For shared directories (both web server and deployer can write)
  • www-data: Common user for web server and deployment process

9. Generate JWT Certificates

Role of JWT Certificates

Certificates for Shopware6 API authentication and session management:

  • API Authentication: Communication between admin panel and storefront
  • Session Management: Managing user login status
  • Security: Independent certificates for each environment ensure security

Generate JWT certificates for Shopware6 API authentication:

sudo -u www-data bash

# Production environment
cd /var/www/shopware-prod/shared/config/jwt
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem

# Staging environment
cd /var/www/shopware-staging/shared/config/jwt
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem

exit

10. Create Environment Configuration Files

Why Environment Configuration Files Matter

Think of these files as your application’s DNA - they define how your Shopware6 behaves in different environments:

  • Security Keys: Unique encryption keys for each environment
  • Database Connections: Separate staging and production databases
  • Performance Settings: Different cache and optimization settings
  • Feature Flags: Enable/disable features per environment
  • External Services: Different API endpoints for testing vs production

Staging Environment Configuration

sudo -u www-data nano /var/www/shopware-staging/shared/.env.local

Enter the following content (change password and secret accordingly):

APP_ENV=staging
SHOPWARE_STAGING=1
APP_SECRET=your-staging-secret-32-chars-long
DATABASE_URL=mysql://shopware:your_secure_password@localhost:3306/shopware_staging
APP_URL=https://staging.yourdomain.com
MAILER_DSN=null://null
SHOPWARE_HTTP_CACHE_ENABLED=1
SHOPWARE_ES_ENABLED=0

Production Environment Configuration

sudo -u www-data nano /var/www/shopware-prod/shared/.env.local

Enter the following content:

APP_ENV=prod
APP_SECRET=your-production-secret-32-chars-long
DATABASE_URL=mysql://shopware:your_secure_password@localhost:3306/shopware
APP_URL=https://yourdomain.com
MAILER_DSN=null://null
SHOPWARE_HTTP_CACHE_ENABLED=1
SHOPWARE_ES_ENABLED=0

11. SSH Configuration and Deployment User Preparation

Smart Deployment User Design

For a perfect balance between security and operational efficiency, here’s our strategy:

  • Use www-data: Same user for web server and deployer avoids permission headaches
  • SSH Key Authentication: Much more secure than password-based access
  • Dedicated Keys: Purpose-built SSH keys make deployment intent crystal clear

Enable SSH access for the www-data user:

# Change shell of www-data
sudo chsh -s /bin/bash www-data

# Create SSH key directory
sudo -u www-data mkdir -p /var/www/.ssh
sudo chmod 700 /var/www/.ssh

Important: By changing the shell of the www-data user, server access from deployer is enabled.

12. Start Services

Service Management Basics

Service management with systemd:

  • enable: Configure automatic start at system boot
  • start: Start service immediately
  • status: Check service operational status
# Start and enable PHP-FPM
sudo systemctl enable php8.3-fpm
sudo systemctl start php8.3-fpm

# Start and enable Caddy
sudo systemctl enable caddy
sudo systemctl start caddy

# Restart MySQL
sudo systemctl restart mysql

# Check service status
sudo systemctl status php8.3-fpm caddy mysql

What’s Next?

In Part 2, we’ll set up the local development environment with Dockware and implement an automated deployment system using GitHub Actions. The next steps include:

  • Local Development Environment: Setup with Docker and Dockware
  • Deployer Configuration: Configure zero-downtime deployment
  • GitHub Actions: CI/CD pipeline for automated deployment
  • Branching Strategy: Staging and production workflows

With the server infrastructure built in this part, we can implement a fully automated deployment system in Part 2.


References