Install and maintain Magento on Ubuntu
Magento is deployed as a production stack with PHP 8.2, MariaDB, OpenSearch 2.x, Redis, Nginx, Varnish, Composer, cron, and TLS.
Before you start
- Use a clean, supported Ubuntu server with root or sudo access.
- Point
app.example.comto the server before requesting a public TLS certificate. - Replace every value written as
CHANGE_MEand store the generated credentials in a password manager. - Take a snapshot before changing an existing installation.
- Valid Magento Marketplace public/private Composer credentials entered interactively or through a protected authentication file.
- At least 4 GB RAM for a small installation; production stores commonly require more.
- A tested compatibility combination for the exact Magento, PHP, OpenSearch, and Composer versions you select.
What the AppStore installation creates
- Magento code under
/var/www/app.example.com/public_html - PHP 8.2 FPM and MariaDB
- OpenSearch 2.x, Redis databases for cache/full-page-cache/sessions
- Nginx, Varnish on port 6081, Magento cron, and Certbot TLS
1. Install the platform services
Install the exact dependency versions validated for your Magento release.
sudo apt update
sudo apt install -y nginx mariadb-server redis-server varnish composer \
php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl php8.2-gd php8.2-intl \
php8.2-mbstring php8.2-soap php8.2-xml php8.2-zip php8.2-bcmath \
certbot python3-certbot-nginx unzip ufw fail2ban
# Install and pin the compatible OpenSearch 2.x release from its signed repository.
sudo systemctl enable --now mariadb redis-server php8.2-fpm opensearch nginx varnish
2. Create the database and Composer authentication
Use unique random passwords. Configure Magento repository authentication with Composer without printing the private key.
sudo mysql
CREATE DATABASE magento CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'magento'@'localhost' IDENTIFIED BY 'CHANGE_ME_DB_PASSWORD';
GRANT ALL PRIVILEGES ON magento.* TO 'magento'@'localhost';
FLUSH PRIVILEGES;
EXIT;
composer config --global --auth http-basic.repo.magento.com PUBLIC_KEY PRIVATE_KEY
3. Create the Magento project and run setup
The playbook uses a domain-specific root and production defaults for ZAR and Africa/Johannesburg.
sudo install -d -o www-data -g www-data -m 0750 /var/www/app.example.com/public_html
cd /var/www/app.example.com
sudo -u www-data composer create-project --repository-url=https://repo.magento.com/ \
magento/project-community-edition public_html
cd public_html
sudo -u www-data bin/magento setup:install \
--base-url=https://app.example.com/ \
--db-host=localhost --db-name=magento --db-user=magento --db-password='CHANGE_ME_DB_PASSWORD' \
--backend-frontname=CHANGE_ME_ADMIN_PATH --admin-firstname=Admin --admin-lastname=User \
--admin-email=admin@example.com --admin-user=CHANGE_ME_ADMIN \
--admin-password='CHANGE_ME_ADMIN_PASSWORD' \
--language=en_US --currency=ZAR --timezone=Africa/Johannesburg --use-rewrites=1 \
--search-engine=opensearch --opensearch-host=127.0.0.1 --opensearch-port=9200
4. Enable Redis, production mode, cron, and web routing
Configure Redis database 0 for cache, 1 for full-page cache, and 2 for sessions; generate Varnish VCL; place Nginx behind Varnish or use the playbook’s port layout.
cd /var/www/app.example.com/public_html
sudo -u www-data bin/magento setup:config:set --cache-backend=redis --cache-backend-redis-server=127.0.0.1 --cache-backend-redis-db=0
sudo -u www-data bin/magento setup:config:set --page-cache=redis --page-cache-redis-server=127.0.0.1 --page-cache-redis-db=1
sudo -u www-data bin/magento deploy:mode:set production
sudo -u www-data bin/magento cron:install
sudo -u www-data bin/magento setup:static-content:deploy -f
sudo -u www-data bin/magento cache:flush
sudo nginx -t
sudo certbot --nginx -d app.example.com -m admin@example.com --agree-tos --redirect
Important files and data
- Magento root:
/var/www/app.example.com/public_html - Application configuration and encryption key:
app/etc/env.php - Generated and media data:
pub/media,var, andgenerated - Nginx, Varnish, OpenSearch, Redis, PHP-FPM, and cron configuration
Health checks and logs
Run these checks after installation and after each upgrade:
cd /var/www/app.example.com/public_html
sudo -u www-data bin/magento status
sudo -u www-data bin/magento cron:run
curl -f http://127.0.0.1:9200
redis-cli ping
sudo nginx -t
sudo systemctl status php8.2-fpm mariadb opensearch redis-server varnish nginx --no-pager
Routine maintenance
Review release notes and take a backup or snapshot before upgrading. Use the following playbook-aligned commands as the starting point:
cd /var/www/app.example.com/public_html
sudo -u www-data bin/magento maintenance:enable
sudo -u www-data composer install --no-dev
sudo -u www-data bin/magento setup:upgrade
sudo -u www-data bin/magento setup:di:compile
sudo -u www-data bin/magento setup:static-content:deploy -f
sudo -u www-data bin/magento cache:flush
sudo -u www-data bin/magento maintenance:disable
Backup scope
- MariaDB database and
app/etc/env.phpwith its encryption key pub/mediaand custom code/themes- Composer files, service configurations, and the exact installed package versions
A usable backup needs both application files and application data. Test restoration on a separate server; an untested backup is not a recovery plan.
Troubleshooting
- Confirm DNS with
dig +short app.example.combefore retrying Certbot. - Test the web-server configuration before reloading it:
sudo nginx -torsudo apache2ctl configtest. - Check free space with
df -hand listening ports withsudo ss -ltnup. - If a service fails, inspect its systemd journal before changing configuration.
- Run Magento commands as the web/application owner to avoid mixed ownership.
- Check OpenSearch cluster health and Magento’s configured search endpoint when catalog search or indexing fails.
- A redirect loop often indicates inconsistent base URLs or TLS headers between Varnish, Nginx, and Magento.
Security notes
- Do not paste passwords, API keys, repository credentials, private keys, or access tokens into tickets or public logs.
- Expose only the documented public ports. Keep database and application backend ports bound to localhost or a private network.
- Keep SSH access working before enabling UFW, then allow only the ports this guide lists.
- Renewal can be tested safely with
sudo certbot renew --dry-runwhere Certbot manages TLS.

