Deploying applications on TurboStack

What every application needs

| Building block | Where it is set | Notes |
|---|---|---|
| **Web server** | `webserver` (host) | `nginx` (default) or `apache2`. |
| **Runtime** | per application: `php_version` / `nodejs_version` / `python_version` / `dotnet_version` | Match the application's supported version. |
| **Database** | `mysql_version` or `postgresql_version` (host) | The database the app stores its data in. |
| **Application type** | per application: `app_type` | Tells TurboStack which application to provision. |
| **Domain + TLS** | per application: `server_name`, `cert_type` | `letsencrypt` gives automatic HTTPS. |
| **Caching / search** | `redis_enabled`, `varnish_enabled`, `elasticsearch_version` | Enable what the app benefits from (see rules below). |


Deploying applications on TurboStack

The base configuration pattern

deploy application turbostack magento shopware wordpress odoo laravel medusa hosting yaml configuration new relic tideways application performance monitoring

Almost every application follows the same skeleton - **global services** on the host, and one or
more **applications** (vhosts) under a **system user**:

```yaml
# ── Host-level services ───────────────────────────────
webserver: nginx               # web server for all sites on this host
mysql_version: "8.4"           # a database engine (or postgresql_version)
redis_enabled: true            # in-memory cache (sessions/objects) - on by default

# ── Accounts and their applications ───────────────────
system_users:
  - username: prod              # the OS account that owns the files
    vhosts:
      - server_name: example.com www.example.com   # the domain(s)
        app_type: wordpress    # which application TurboStack provisions
        php_version: "8.4"     # the runtime version for this site
        cert_type: letsencrypt # automatic HTTPS certificate
```

> [!NOTE]
> Defaults are auto-tuned (memory sizing for databases, cache and search). Only override sizing
> keys (`mysql_innodb_size`, `redis_memory`, `elasticsearch_heap_size`, `varnish_cache_size`)
> when you have measured a real performance need.

> [!TIP]
> Name the first application of each system user `default`. This is the convention TurboStack
> expects for the primary application under a user.

> [!WARNING]
> Run staging and production on entirely different servers. A staging site on the same host
> silently takes resources away from production - even when it is rarely used, it still consumes
> memory (for example for its database), causing avoidable overhead on your production site.


Deploying applications on TurboStack

How to choose services

These rules let you (or an AI assistant) build a correct configuration for any application:

- **The database follows the application.** PHP CMS/shop apps (WordPress, Drupal, Magento,
  Shopware, Craft CMS) use **MySQL** (`mysql_version`). **Odoo** and **Medusa** use
  **PostgreSQL** (`postgresql_version`). Akeneo, Craft CMS and Nextcloud support either.
- **The runtime follows the application.** PHP apps set `php_version`; **Medusa** (Node) sets
  `nodejs_version`; **Odoo** (Python) needs no version key; **nopCommerce** (.NET) sets
  `dotnet_version`.
- **Redis** (`redis_enabled: true`) is recommended for almost everything - sessions and object
  cache - and is on by default.
- **Elasticsearch/OpenSearch** (`elasticsearch_version`) is **required by Magento 2** and used
  by **Akeneo**; most other apps don't need it.
- **Varnish** (`varnish_enabled: true`, per application) is full-page caching for **PHP storefronts**
  (Magento, Shopware, optionally WordPress). Don't use it for Node apps or Odoo.
- **Node and Odoo run as their own process** behind nginx. For **Medusa** (and other Node apps)
  set `proxy_enabled: true` and `proxy_upstream_port`. **Odoo** uses `app_type: odoo` (proxying
  is handled for you).
- **Transport Layer Security (TLS)** is almost always `cert_type: letsencrypt` (automatic HTTPS).


Deploying applications on TurboStack

Application performance monitoring

New Relic and Tideways are set **per application**, not per host. Open the application's
**Configure application > Monitoring** tab, turn on **New Relic APM** or **Tideways**, and paste the
key from your own account. TurboStack installs and configures the agent for that application only;
the subscription and the data stay with your monitoring account. Turning the toggle off again
removes the keys.

```yaml
system_users:
  - username: prod
    vhosts:
      - server_name: example.com
        app_type: magento2
        php_version: "8.4"
        # New Relic: reports this application under its own name
        newrelic_appname: "Example production"
        newrelic_license: "nr_xxxxxxxxxxxx"
        # Tideways: instead of, or next to, New Relic
        tideways_apikey: "tw_xxxxxxxxxxxx"
        tideways_service: web
        tideways_sample_rate: 25
```

| Key | What it sets |
|---|---|
| `newrelic_appname` | The name this application reports under in New Relic. |
| `newrelic_license` | The license key of your New Relic account. Setting it enables the agent for this application. |
| `tideways_apikey` | The key that sends profiling data to your Tideways account. |
| `tideways_service` | The service name this application reports under, so several applications stay apart. |
| `tideways_sample_rate` | The percentage of requests that is profiled. Lower it on a busy site to reduce overhead. |

> [!NOTE]
> Set a different `newrelic_appname` and `tideways_service` per application, and per environment.
> Otherwise staging and production data end up in the same graph.

**Blackfire** works differently: it is available on the host and you start a profiling session
yourself, from the browser extension or with `tscli blackfire enable`. The server-level agent that
reports the machine's own metrics to your New Relic account is set on the host's
Advanced tab. See
Monitoring for what each product is good at.


Deploying applications on TurboStack

Application catalog

> [!NOTE]
> Each application has three pages: **Deploy** (this catalog links to it), **Best practices** (performance & stability) and **Troubleshooting** (logs & common fixes).

| Application | Type | Runtime | Database | Notable services |
|---|---|---|---|---|
| WordPress | `wordpress` | PHP | MySQL | Redis; Varnish (optional) |
| Magento 2 / Adobe Commerce | `magento2` | PHP | MySQL | Elasticsearch, Redis, Varnish |
| Shopware | `shopware` | PHP | MySQL | Redis, Varnish, OpenSearch (optional) |
| Drupal | `drupal` | PHP | MySQL | Redis; Varnish (optional) |
| Laravel | `laravel` | PHP | MySQL | Redis (optional) |
| Akeneo PIM | `akeneo` | PHP | MySQL/PostgreSQL | Elasticsearch |
| OroCommerce | `orocommerce` | PHP | MySQL/PostgreSQL | Redis, RabbitMQ; Varnish (optional) |
| Craft CMS | `craftcms` | PHP | MySQL/PostgreSQL | Redis; Varnish (optional) |
| Nextcloud | `nextcloud` | PHP | MySQL/PostgreSQL | Redis |
| Odoo | `odoo` | Python | PostgreSQL | - (runs behind Nginx) |
| Medusa | Node (proxy) | Node.js | PostgreSQL | reverse proxy |
| nopCommerce | `nopcommerce` | .NET | SQL Server | - |
| Self-hosted platforms | `gitlab`, `pmm` | Kubernetes | - | GitLab, Advanced Database Monitoring |

> [!TIP]
> Don't see your exact framework? Use **Generic** (`app_type` left empty) for a plain PHP site,
> or the **reverse-proxy** pattern for any Node/containerized app - see
> Medusa and Applications.


Deploying applications on TurboStack

File layout and permissions

Once an application is deployed, its files live under your system user's home directory, and you work with them over Secure Shell (SSH). `~` below is that home directory, for example `/var/www/prod/` for the system user `prod`.

| Path | What it is |
| --- | --- |
| `~/` | Your system user's home directory |
| `~/public_html/` | The application root and web document root for most applications |
| `~/nginx/` | Your custom Nginx configuration snippets |
| `~/logs/` | Application and access logs |
| `~/.config/systemd/user/` | Your systemd user service unit files |

> [!NOTE]
> Some applications use their own document root inside the application root (for example Magento serves from `~/public_html/pub/`). Each application's **Reference** page lists its exact layout.


Deploying applications on TurboStack

Ownership and permissions

Your system user must own all application files, so updates and package installs do not fail on permission errors. Files should be readable and directories listable, without being world-writable. Run these from your application root, replacing `prod` with your system user:

```bash
find ~/public_html -type f -exec chmod 644 {} \;   # files: readable
find ~/public_html -type d -exec chmod 755 {} \;   # directories: listable
chown -R prod:prod ~/public_html                   # your user owns everything
```

> [!WARNING]
> Do not set `777` permissions to fix an access problem. World-writable files are a security risk. Set the ownership to your system user instead.


Deploying applications on TurboStack

Composer and caches

Applications that use Composer (the PHP dependency manager) install from the application root; after a change, clear the relevant caches with the TurboStack CLI:

```bash
cd ~/public_html && composer install   # or: composer update (test on staging first)
tscli redis clear                       # clear the Redis cache
tscli varnish clear                     # clear the Varnish full-page cache
```

> [!WARNING]
> `tscli redis clear` and `tscli varnish clear` flush live caches. Expect a short spike in load while the caches refill.


Deploying applications on TurboStack

Related

- Applications (host tab) - the GUI where you configure this.
- Services - the databases, caching and search engines.
- The Source (YAML) view - edit the configuration as YAML.
- Monitoring - New Relic, Tideways, Blackfire and database monitoring.
- Publishing changes - deploy it to the server.
- Glossary - what the terms and abbreviations mean.
- Sales - help sizing the platform for your application.


Laravel best practices

laravel performance laravel optimization laravel caching laravel turbostack Redis queues opcache php-fpm config cache

# Laravel best practices

This page collects practical advice for running a fast, stable Laravel application on TurboStack. Laravel is a configuration-only application: the platform provisions PHP, the web server, MySQL and (optionally) Redis, while you bring and deploy your own code through Git deployment. Most performance work therefore happens in your repository, while the platform provides the runtime and services to support it.

> [!NOTE]
> Because you control the code, the platform never runs Artisan commands for you. Compiling caches, running migrations and starting workers are steps you build into your own deploy process.


Laravel best practices

What TurboStack configures for you

When you set an app's type to `laravel`, the platform applies an opinionated runtime so the framework works without manual web-server editing:

- **Nginx vhost** with the document root pointing at the application's public directory (`public_html`) and the standard front-controller rewrite (`try_files $uri $uri/ /index.php?$query_string`). This ensures clean URLs and route handling work without additional configuration.
- **PHP-FPM backend** per application, with a generous FastCGI timeout for longer requests and dotfile protection (everything under `/.` except `/.well-known` is denied).
- **OPcache** available in the PHP runtime to cache compiled bytecode.
- **MySQL** as the relational database, and **Redis** when you enable it as a service for cache, sessions and queues.
- **Supervisor**, available per system user, for keeping long-running processes such as queue workers alive.

Manage these building blocks from the host's Services and Applications tabs.


Laravel best practices

Recommended optimizations

Combine Laravel's own production guidance with the platform features above.

- **Cache configuration and routes** - run `php artisan config:cache` and `php artisan route:cache` in your deploy so framework bootstrapping is fast.
- **Cache views and events** - add `php artisan view:cache` (and `event:cache` if used) to precompile Blade templates and listeners.
- **Use the optimize shortcut** - `php artisan optimize` bundles the common production caches in one command.
- **Enable OPcache** - keep OPcache on in your PHP runtime; tune it under Services > PHP advanced options.
- **Move cache and sessions to Redis** - set `CACHE_STORE=redis` and `SESSION_DRIVER=redis` once Redis is enabled, to offload the database and the local filesystem.
- **Run queues on Redis** - use `QUEUE_CONNECTION=redis` and process jobs with a worker rather than the `sync` driver for slow tasks (mail, exports, webhooks).
- **Keep workers alive** - run `php artisan queue:work` under a supervisor so jobs process continuously and restart on failure. Use the per-user Supervisor, or run the worker as a user system service. To clear a backlog faster, run several workers in parallel - but keep the total within the host's processor budget (see Scale throughput with more instances).
- **Schedule with the scheduler** - point a single cron entry at `php artisan schedule:run` every minute and define tasks in Laravel rather than many crontab lines.
- **Optimize the autoloader** - deploy with `composer install --no-dev --optimize-autoloader` to drop dev dependencies and speed up class loading.
- **Optimize assets** - build front-end assets ahead of deploy (e.g. `npm run build` with Vite) and serve them as static files; add a Content Delivery Network (CDN) for heavy media.

> [!TIP]
> After any code or config change, re-run the cache commands. Stale `config:cache` output is a common cause of "my `.env` change had no effect".


Laravel best practices

Sizing and scaling

TurboStack auto-tunes service memory from the host's resources, so the defaults suit most Laravel sites. Override the tuning variables only when you have measured evidence (slow queries, Redis evictions, OPcache restarts):

| Variable | Tune when |
| --- | --- |
| `mysql_innodb_size` | The InnoDB buffer pool is too small for your working set. |
| `redis_memory` | Redis is evicting keys you expect to persist (cache/queue churn). |

Make one change at a time and re-measure. See Performance tuning for the full method, and scale PHP-FPM workers there too.


Laravel best practices

Stability

- **Back up before changes** - confirm Backups cover the database and any user-uploaded files in `storage/`.
- **Watch Health** - monitor CPU, memory and service status, especially after a deploy.
- **Keep versions current** - track Laravel long-term support (LTS) and PHP releases, and bump the PHP runtime under Services on a supported version.
- **Test on a staging clone** - validate upgrades, migrations and cache changes on a copy before publishing to production.
- **Run migrations deliberately** - include `php artisan migrate --force` in your deploy step and review it for destructive changes first.


Laravel best practices

Related

- Deploy Laravel on TurboStack
- Troubleshooting Laravel
- How to manage user system services
- Services
- Performance tuning


Deploy Laravel on TurboStack

deploy laravel laravel hosting laravel turbostack php 8.4 mysql Redis queues git deployment

# Deploy Laravel on TurboStack

Laravel is a PHP framework for building web applications and APIs. On TurboStack the platform provisions the PHP runtime, MySQL database, and Redis from your host configuration, and you deploy by publishing.

> [!NOTE]
> This is a configuration-only application: you bring your own application code. Deploy it with Git deployment.


Deploy Laravel on TurboStack

Requirements

| Requirement | Recommended |
| --- | --- |
| App type | `laravel` |
| Runtime | PHP 8.4 (or newer) |
| Database | MySQL 8.4 |
| Cache (Redis) | Enabled (queues, cache, sessions; optional) |
| Web server | Nginx |


Deploy Laravel on TurboStack

Configure it

1. Open the host's Applications tab.
2. Click **Add app or database**.
3. Set **App Type** to `laravel` and choose the PHP runtime under **Technologies**.
4. Enable MySQL, and optionally Redis, as services.
5. Publish, then deploy your code via Git deployment.


Deploy Laravel on TurboStack

Example configuration

```yaml
webserver: nginx
mysql_version: "8.4"           # application database
redis_enabled: true          # queues, cache, sessions (optional but common)
system_users:
  - username: prod
    vhosts:
      - server_name: app.example.com
        app_type: laravel
        php_version: "8.4"
        cert_type: letsencrypt
```


Deploy Laravel on TurboStack

Why these choices

- **MySQL 8.4** is a common relational backend for Laravel's Eloquent object-relational mapping (ORM) layer and migrations.
- **Redis** is optional but widely used in Laravel for queues, cache, and session storage; enable it when your app uses any of those drivers.
- **Nginx** serves the application's public directory and proxies to PHP-FPM.
- **Let's Encrypt** provides automatic, renewing HTTPS.
- **You bring your own code**, so the application is deployed by you rather than scaffolded by the platform.


Deploy Laravel on TurboStack

Related

- Technologies used: PHP, MySQL, Redis.

- Best practices - performance & stability.
- Troubleshooting - logs & common fixes.
- Applications overview
- Services
- Applications (host tab)
- The Source (YAML) view


Troubleshooting Laravel

laravel troubleshooting laravel logs laravel error laravel turbostack http 500 app_key php artisan cache queue worker

# Troubleshooting Laravel

When a Laravel application is not working correctly on TurboStack, the cause is almost always visible in a log. This page shows where the logs live, the most common problems and their fixes, and a repeatable workflow to follow. Because Laravel is a configuration-only application, most fixes are Artisan commands or `.env` changes you run against your own deployed code.


Troubleshooting Laravel

Where to find the logs

Replace `<user>` with the host's system user and `<app>` with the application name (omit the `_<app>` suffix if the app has no name set).

| Component | Where |
| --- | --- |
| Nginx access log | `/var/log/nginx/<user>_<app>.log` |
| Nginx error log | `/var/log/nginx/error.log` |
| PHP / PHP-FPM errors | `/var/log/php/<user>_<app>.log` |
| Laravel application log | `/var/www/<user>/<app>/storage/logs/laravel.log` |
| Queue workers (supervisor) | `/var/www/<user>/.config/supervisor/log/` |
| Database | Managed MySQL service - see Services |

Also use the host's Health tab for service status and resource graphs, and check recent deploys in History - a broken page often coincides with the last publish.

> [!TIP]
> The Laravel log only fills up if logging works. If `storage/logs/laravel.log` is empty during a 500, the error happened before Laravel booted - look in the PHP-FPM log instead.


Troubleshooting Laravel

Common issues

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| HTTP 500, blank page | Application exception; details suppressed in production | Read `storage/logs/laravel.log`, then the PHP-FPM log; fix the underlying error. |
| "No application encryption key" / 500 on boot | Missing or invalid `APP_KEY` | Set a valid key with `php artisan key:generate`, then `php artisan config:clear`. |
| `.env` change has no effect | Stale compiled config cache | Run `php artisan config:clear` (and re-run `config:cache` after). |
| Old routes/views served, "class not found" | Stale route/view/autoload caches after deploy | `php artisan route:clear && php artisan view:clear && php artisan cache:clear`; `composer dump-autoload`. |
| Database / migration errors | Pending migrations or wrong DB credentials | Verify `.env` DB settings against Services; run `php artisan migrate --force`. |
| Jobs never run, queue backs up | No worker running, or wrong queue connection | Check `QUEUE_CONNECTION`; start `php artisan queue:work` under supervisor and inspect its log. |
| Scheduled tasks not firing | Scheduler cron not calling Laravel | Ensure a cron entry runs `php artisan schedule:run` every minute. |
| "Permission denied" writing cache/logs | `storage/` or `bootstrap/cache/` not writable | Make those directories writable by the system user; clear and rebuild caches. |

> [!WARNING]
> Never set `APP_DEBUG=true` to read errors on a live site - it exposes stack traces and secrets. Read the logs instead and keep `APP_DEBUG=false` in production.


Troubleshooting Laravel

A troubleshooting workflow

1. **Check Health** - confirm the host is up and PHP, MySQL and Redis services are running before digging into code.
2. **Read the relevant log** - start with `storage/logs/laravel.log`; if it is silent, read the PHP-FPM log (`/var/log/php/<user>_<app>.log`) and the Nginx error log.
3. **Check the last deploy** - review recent changes in History. If a publish broke the site, revert or re-publish a known-good revision via Publishing.
4. **Verify services and caches** - confirm queue workers are alive in supervisor, then clear and rebuild Laravel caches (`php artisan optimize:clear` followed by `php artisan optimize`) once the fix is in place.


Troubleshooting Laravel

Getting help

If you have worked through the steps above and the problem persists, see the platform-wide Troubleshooting guide. Then reach out through Support with the relevant log excerpts and the time the issue occurred.


Troubleshooting Laravel

Related

- Deploy Laravel on TurboStack
- Laravel best practices
- Health
- Support


Magento best practices

magento performance magento optimization magento caching magento turbostack adobe commerce Varnish full-page cache Redis Elasticsearch opcache

# Magento best practices

Magento 2 (Adobe Commerce) is resource-intensive, placing high demands on memory, CPU, and I/O. A fast, stable storefront depends on the right caching layers, a working cron, and production mode. TurboStack provisions most of this for you; this page covers what is already configured and what to optimize on top.


Magento best practices

What TurboStack configures for you

When you deploy a `magento2` app, the platform sets up a complete, tuned Magento stack:

- **Document root** - the vhost serves from the Magento `pub/` directory inside your app root (`/var/www/<user>/public_html`), with a Magento-specific Nginx configuration (`50main.conf`).
- **Redis caching** - sessions use a dedicated persistent Redis socket, while the default cache and full-page cache use a separate Redis instance. This is configured at install time, so checkout state stays fast and the database is offloaded.
- **Varnish full-page cache** - when Varnish is enabled, the platform installs a Magento-aware VCL. It configures Magento to use it (`http-cache-hosts`, `caching_application = 2`, backend on port 8080, Varnish on 6081, default Time to Live (TTL) 86400s).
- **Elasticsearch/OpenSearch** - configured as the catalog search engine with a per-site index prefix. Magento requires this; the storefront will not run without it.
- **Cron jobs** - three cron entries are installed for the system user: the main `bin/magento cron:run`, `setup:cron:run`, and the updater cron. These drive indexing, emails, and scheduled jobs.
- **Log rotation** - everything under the app's `var/log/` is rotated weekly via logrotate so disks do not fill up.
- **Database tuning** - a Magento-specific MySQL setting (`restrict_fk_on_non_standard_key = OFF`) is applied for compatibility, and OPcache is enabled in the PHP runtime.


Magento best practices

Recommended optimizations

Combine Magento's vendor guidance with the TurboStack options below. Most are one toggle away on the host's Services tab.

- **Run in production mode** - set the deploy mode to `production` for live stores: `bin/magento deploy:mode:set production`. This compiles dependency injection (DI) and pre-generates static assets. After deploying code or configuration changes, run the standard sequence `setup:upgrade` -> `setup:di:compile` -> `setup:static-content:deploy` -> `cache:flush` (use `setup:upgrade --keep-generated` on production to preserve already-compiled code).
- **Enable Varnish full-page cache** - this reduces time-to-first-byte significantly for storefront pages; keep it on for production.
- **Keep Redis on for sessions and cache** - already configured; do not switch back to file sessions on a busy store. See Redis cache tuning for the `env.php` cache block and its lifetime settings.
- **Keep Elasticsearch/OpenSearch healthy** - it is required; size it for your catalog (see below).
- **Enable OPcache** (and consider the realpath cache) in the PHP runtime to cut PHP parsing overhead.
- **Optimize assets and images** - minify/merge JS and CSS, use WebP/optimized images, and serve `pub/static` and `pub/media` via a Content Delivery Network (CDN) when possible.
- **Use an HTTP cache / CDN in front** - offload static and media delivery. If a CDN fronts the site, TurboStack can disable Varnish caching of static/media to avoid double caching.
- **Keep cron running** - reindexing, price rules, emails, and message-queue consumers all depend on cron; never disable it on a live store.
- **Run message-queue consumers as services** - on a busy store, run consumers as persistent `systemd --user` services rather than relying only on cron, so they restart on failure. See Message-queue consumers below.


Magento best practices

Redis cache tuning

TurboStack configures Magento's Redis backends at install time, so you do not set this up by hand. Sessions use the persistent Redis instance; the default cache and full-page cache use the cache instance. The reference below shows the cache block in `app/etc/env.php` and the values worth tuning (`Cm_Cache_Backend_Redis`, port 6379, no password):

```php
<?php
return [
    'cache' => [
        'frontend' => [
            'default' => [
                'backend' => 'Cm_Cache_Backend_Redis',
                'backend_options' => [
                    'server' => '127.0.0.1',
                    'port' => '6379',
                    'database' => '0',
                    'id_prefix' => 'magento_prod_',
                    'compress_data' => '1',
                    'default_lifetime' => '600',
                    'min_lifetime' => '60',
                    'max_lifetime' => '86400',
                ],
            ],
            'page_cache' => [
                'backend' => 'Cm_Cache_Backend_Redis',
                'backend_options' => [
                    'server' => '127.0.0.1',
                    'port' => '6379',
                    'database' => '1',
                    'id_prefix' => 'magento_prod_',
                    'compress_data' => '0',
                    'default_lifetime' => '86400',
                ],
            ],
        ],
    ],
];
```

- `database` keeps the two caches apart (`0` for the default cache, `1` for the full-page cache); `id_prefix` keeps keys unique when several sites share the instance.
- `default_lifetime` is the Time to Live (TTL) in seconds for keys with no explicit expiry. `min_lifetime` and `max_lifetime` bound the lifetime for the default cache.
- `compress_data` set to `1` shrinks the cache at a small processing cost. It is on for the default cache and off for the full-page cache, which is already compact.

After changing `env.php`, flush the cache:

```bash
php bin/magento cache:flush
```

If caching was not enabled before, turn it on so Redis is actually used:

```bash
php bin/magento cache:enable
```


Magento best practices

Message-queue consumers

Magento offloads asynchronous work - reindexing, transactional emails, and order or coupon processing - to message queues handled by **consumer** processes. On a busy store, run each consumer as a persistent `systemd --user` service so it restarts on failure, instead of relying on cron alone.

Create a reusable template unit at `~/.config/systemd/user/magento-consumer@.service`:

```ini
[Unit]
Description=Magento Consumer (%i)
After=network-online.target
Requires=dbus.socket
StartLimitIntervalSec=0

[Service]
Type=simple
WorkingDirectory=%h/public_html
ExecStart=php %h/public_html/bin/magento queue:consumers:start %I --single-thread --max-messages=10000
RestartSec=10s
Restart=always

[Install]
WantedBy=default.target
```

- `%i` is the consumer name, so one template serves every queue, and `%h` is your home directory.
- `--single-thread` runs one thread per process; `--max-messages=10000` restarts the consumer periodically to keep memory in check.

List the available consumers with `php bin/magento queue:consumers:list`, then enable and start one service per consumer (the name after `@` is the consumer):

```bash
systemctl --user enable --now magento-consumer@sales.rule.update.coupon.usage.service
systemctl --user status magento-consumer@product_action_attribute.update.service
```

To drain queues faster on a busy store, run several consumers in parallel (more instances, or more of the same consumer). Keep the total number of consumers within the host's processor budget: never more than the core count, and keep a margin. Too many will slow the whole store. See Scale throughput with more instances.

For the `systemd --user` basics - including `loginctl enable-linger` so the services keep running after you log out - see How to manage user system services.


Magento best practices

Sizing and scaling

TurboStack auto-tunes resource limits from the host's size, so start with the defaults. Override the tuning variables only when you have measured evidence (slow queries, cache evictions, search latency):

| Variable | Tune when |
| --- | --- |
| `mysql_innodb_size` | The InnoDB buffer pool is too small for your catalog/order volume |
| `redis_memory` | Redis is evicting keys under load |
| `elasticsearch_heap_size` | Search is slow or the catalog is very large |
| `varnish_cache_size` | The page cache hit ratio is low because the cache is too small |

See Performance tuning before changing any of these. When a single host can no longer keep up, scale up the host before splitting services.


Magento best practices

Stability

- **Back up regularly** - verify scheduled Backups cover both the database and the `pub/media`, `app/etc`, and `var` directories.
- **Watch Health** - monitor CPU, memory, and disk. Magento's `var/` and media directories grow over time.
- **Keep versions current** - track Magento/Adobe Commerce security patches and the PHP, MySQL, and Elasticsearch versions you run.
- **Test on a staging clone** - always trial upgrades, extensions, and theme changes on a copy before publishing to production.


Magento best practices

Related

- Deploy Magento
- Magento reference
- How to run multiple Magento store views
- Troubleshooting Magento
- How to manage user system services
- Services
- Performance tuning


Deploy Magento on TurboStack

deploy magento magento hosting magento turbostack adobe commerce php 8.4 Elasticsearch Redis Varnish full-page cache

# Deploy Magento on TurboStack

Magento 2 (Adobe Commerce) is an e-commerce platform with high infrastructure requirements. On TurboStack, the platform provisions the PHP runtime, MySQL database, Elasticsearch, and caching services from your host configuration. You deploy by publishing.


Deploy Magento on TurboStack

Requirements

| Requirement | Recommended |
| --- | --- |
| App type | `magento2` |
| Runtime | PHP 8.4 (or newer) |
| Database | MySQL 8.4 |
| Search | Elasticsearch 8.x (REQUIRED) |
| Cache (Redis) | Enabled (sessions + object cache) |
| Page cache (Varnish) | Enabled (recommended) |
| Web server | Nginx |

> [!NOTE]
> TurboStack can run an automated Magento install. Set `magento2_version_to_install` and provide your Magento Marketplace composer credentials with `magento2_auth_public_key` and `magento2_auth_private_key`.


Deploy Magento on TurboStack

Configure it

1. Open the host's Applications tab.
2. Click **Add app or database**.
3. Set **App Type** to `magento2` and choose the PHP runtime under **Technologies**.
4. Enable MySQL, Elasticsearch, Redis, and Varnish as services.
5. Publish to apply the configuration.


Deploy Magento on TurboStack

Example configuration

```yaml
webserver: nginx
mysql_version: "8.4"           # Magento core database
elasticsearch_version: "8.x"  # REQUIRED by Magento for catalog search
redis_enabled: true            # sessions + object cache
system_users:
  - username: prod
    vhosts:
      - server_name: shop.example.com www.shop.example.com
        app_type: magento2
        php_version: "8.4"
        cert_type: letsencrypt
        varnish_enabled: true  # full-page cache - big TTFB improvement for storefronts
```


Deploy Magento on TurboStack

Why these choices

- **MySQL 8.4** holds the Magento core database: catalog, orders, customers, and configuration.
- **Elasticsearch 8.x is required**; Magento uses it for catalog and layered-navigation search, and the storefront will not function without it.
- **Redis** is used for both sessions and the object cache, keeping checkout state fast and offloading the database.
- **Varnish full-page cache** is recommended because it serves cached storefront pages directly, producing a large time-to-first-byte improvement.
- **Nginx** is the supported web server for Magento and integrates with its built-in caching layers.


Deploy Magento on TurboStack

Related

- Technologies used: PHP, MySQL, Redis, Varnish, Elasticsearch.

- Best practices - performance & stability.
- Magento command-line reference - `php bin/magento` over SSH.
- Troubleshooting - logs & common fixes.
- Applications overview
- Services
- Applications (host tab)
- The Source (YAML) view


Magento command-line reference

magento cli bin/magento php bin/magento magento commands cache clean indexer reindex setup upgrade deploy mode

# Magento command-line reference

Magento 2 (Adobe Commerce) ships a command-line interface (CLI) at `bin/magento`. You run it over SSH to install modules, clear caches, rebuild indexes and switch deployment modes. This page groups the common commands by task. For where the files live and the scheduled jobs, see Magento reference.


Magento command-line reference

How to run it

Connect over SSH, change into the project root, then call the tool with PHP:

```bash
cd ~/public_html
php bin/magento <command>
```

Here `~` is your system user's home directory (for example `/var/www/prod/`). Run every `bin/magento` command from `~/public_html`.

> [!NOTE]
> To run a command with a specific Portable Hypertext Preprocessor (PHP) version, call that version's binary directly. Each installed version has its own binary at `/usr/bin/php<version>`, for example:
> ```bash
> /usr/bin/php8.4 bin/magento setup:upgrade
> ```
> See How to switch PHP version.

Run `php bin/magento list` for the full command list, and `php bin/magento help <command>` for the options of a single command.


Magento command-line reference

Setup and maintenance

| Command | What it does |
| --- | --- |
| `setup:install` | Installs Magento with DB and admin configuration. |
| `setup:uninstall` | Uninstalls Magento and removes configuration. |
| `setup:upgrade` | Applies DB schema/data changes from modules. |
| `setup:upgrade --keep-generated` | Keeps generated code during upgrade (for production). |
| `setup:di:compile` | Compiles dependency injection code. |
| `setup:static-content:deploy` | Deploys static view files for frontend. |
| `setup:static-content:deploy -f` | Forces deployment even in developer mode. |
| `setup:config:set` | Sets configuration values like DB or admin URL. |
| `setup:db:status` | Shows database upgrade status. |
| `setup:db:status --show-config` | Shows current DB connection settings. |
| `maintenance:enable` / `maintenance:disable` / `maintenance:status` | Enables, disables maintenance mode, or shows its status. |


Magento command-line reference

Cache management

| Command | What it does |
| --- | --- |
| `cache:clean` | Clears enabled cache types. |
| `cache:flush` | Clears all cache backends (e.g., Redis). |
| `cache:enable` / `cache:disable` | Enables or disables specified cache types. |
| `cache:status` | Displays cache types and their status. |


Magento command-line reference

Index management

| Command | What it does |
| --- | --- |
| `indexer:reindex` | Rebuilds indexers (e.g. product/category data). |
| `indexer:reindex catalogsearch_fulltext` | Reindexes product search data. |
| `indexer:status` | Shows indexer status. |
| `indexer:reset` | Resets indexers that failed. |
| `indexer:info` | Lists all indexers. |
| `indexer:show-mode` | Displays indexer mode (schedule/realtime). |
| `indexer:set-mode {schedule\|realtime} [indexer]` | Sets mode for a specific indexer. |


Magento command-line reference

Module management

| Command | What it does |
| --- | --- |
| `module:status` | Lists all modules and their status. |
| `module:enable <Module_Name>` | Enables a module. |
| `module:disable <Module_Name>` | Disables a module. |
| `module:uninstall <Module_Name>` | Uninstalls a module and optionally removes its data. |


Magento command-line reference

Admin users

| Command | What it does |
| --- | --- |
| `admin:user:create` | Creates a new admin user. |
| `admin:user:unlock <username>` | Unlocks a locked admin account. |
| `admin:user:delete <username>` | Deletes an admin user. |


Magento command-line reference

Security

| Command | What it does |
| --- | --- |
| `security:passwords:upgrade` | Upgrades customer password hash format. |
| `security:hash:upgrade` | Upgrades internal hash algorithms for better security. |


Magento command-line reference

Deployment mode and developer tools

| Command | What it does |
| --- | --- |
| `deploy:mode:show` | Shows current deployment mode. |
| `deploy:mode:set {developer\|production\|default}` | Changes deployment mode. |
| `deploy:mode:set production --skip-compilation` | Skips DI compilation during mode switch. |
| `dev:profiler:enable` / `dev:profiler:disable` | Enables or disables the performance profiler. |
| `dev:template-hints:enable` / `dev:template-hints:disable` | Displays or hides template hints in frontend. |
| `dev:source-theme:deploy` | Deploys source files (e.g. LESS) for custom themes. |


Magento command-line reference

Store configuration

| Command | What it does |
| --- | --- |
| `config:set <path> <value>` | Sets a configuration value. |
| `config:set catalog/search/engine elasticsearch7` | Sets Elasticsearch as the search engine. |
| `config:show <path>` | Shows a configuration value. |
| `config:sensitive:set <path> <value>` | Marks config value as sensitive (e.g., passwords). |
| `config:sensitive:remove <path>` | Removes sensitive flag from a config. |


Magento command-line reference

EAV attributes

Magento stores product attributes in an Entity-Attribute-Value (EAV) model.

| Command | What it does |
| --- | --- |
| `eav:attribute:remove <attribute_code>` | Removes a custom attribute from the EAV model. |


Magento command-line reference

Inventory reservations

| Command | What it does |
| --- | --- |
| `inventory:reservation:list-inconsistencies` | Lists mismatches in stock reservations. |
| `inventory:reservation:create-compensations` | Fixes reservation issues by creating compensation records. |


Magento command-line reference

Logs

| Command | What it does |
| --- | --- |
| `dev:log:clean` | Clears system and exception logs. |


Magento command-line reference

Testing

| Command | What it does |
| --- | --- |
| `dev:tests:run {unit\|integration\|functional}` | Runs specified test suite. |


Magento command-line reference

Backup and restore

| Command | What it does |
| --- | --- |
| `setup:backup --code --media --db` | Creates backup of codebase, media, and DB. |
| `setup:rollback --code-file=<file> --db-file=<file>` | Restores from specified backup files. |

> [!WARNING]
> The `setup:backup` and `setup:rollback` commands are deprecated in current Magento releases. For a reliable, off-host copy of your store, use the platform backups instead.


Magento command-line reference

Cron and message-queue consumers

| Command | What it does |
| --- | --- |
| `cron:run` | Executes scheduled cron jobs immediately. |
| `cron:install` | Installs Magento cron jobs into system crontab. |
| `queue:consumers:list` | List the message-queue consumers |
| `queue:consumers:start <name>` | Start a single consumer |

TurboStack installs the Magento cron entries for your system user automatically. On a busy store, run each message-queue consumer as a persistent user service instead of relying on cron alone - see Magento best practices.

> [!TIP]
> After deploying code on a production store, run the commands in this order: `setup:upgrade --keep-generated` -> `setup:di:compile` -> `setup:static-content:deploy` -> `cache:flush`.

> [!NOTE]
> The `n98-magerun2` tool is also installed on your host as `magerun2`. It adds convenience commands (for example `magerun2 sys:info`) on top of `bin/magento`. Run `magerun2 list` to see them.

> [!WARNING]
> `cache:flush`, `maintenance:enable` and `deploy:mode:set` change how the live storefront behaves. Run them during a quiet window and confirm the store still serves pages afterwards.


Magento command-line reference

Related

- Magento reference
- Deploy Magento on TurboStack
- Magento best practices
- How to run multiple Magento store views
- Troubleshooting Magento
- How to switch PHP version
- Connect over SSH


How to run multiple Magento store views

magento multiple store views magento multi-website MAGE_RUN_CODE MAGE_RUN_TYPE mage.runmaps multi-domain magento

# How to run multiple Magento store views

One Magento installation can serve several store views or websites, each on its own domain. Magento selects which one to load from two variables, `MAGE_RUN_CODE` (the store-view or website code) and `MAGE_RUN_TYPE` (`store` or `website`). On TurboStack you set them per domain in your Nginx configuration.


How to run multiple Magento store views

Before you start

- **Your store views or websites already created** in Magento (**Stores > All Stores**), each with its code.
- **Secure Shell (SSH) access to the host** - see SSH access. You edit files under your system user's `~/nginx/` directory.


How to run multiple Magento store views

Map each domain to a store code

Create `~/nginx/mage.runmaps` and map each host name to its code and type. `MAGE_RUN_TYPE` is either `store` (a store view) or `website`:

```nginx
map $http_host $MAGE_RUN_CODE {
    hostnames;
    .domain.nl  domainnl;
    .domain.fr  domainfr;
}

map $http_host $MAGE_RUN_TYPE {
    hostnames;
    .domain.nl  store;
    .domain.fr  store;
    default     website;
}
```

Replace `domainnl` and `domainfr` with your store-view or website codes from **Stores > All Stores**.


How to run multiple Magento store views

Pass the variables to Magento

In `~/nginx/50main.conf`, find the two commented `fastcgi_param` lines in the PHP location block and remove the leading `#` so they are active:

```nginx
fastcgi_param MAGE_RUN_CODE $MAGE_RUN_CODE if_not_empty;
fastcgi_param MAGE_RUN_TYPE $MAGE_RUN_TYPE if_not_empty;
```

Publish the host (or reload Nginx with `tscli nginx reload`) to apply the change, then load each domain and confirm it shows the right store view.

> [!NOTE]
> If the host already has store-code mappings for another system user, keep them separate by appending your user name to the variables: use `$MAGE_RUN_CODE_<username>` and `$MAGE_RUN_TYPE_<username>` in both `mage.runmaps` and `50main.conf`.


How to run multiple Magento store views

Verify

List the configured base URLs per store to confirm each code resolves to the right domain:

```bash
magerun2 sys:store:config:base-url:list
```

> [!NOTE]
> `magerun2` (n98-magerun2) is a third-party command-line tool, not part of core Magento.


How to run multiple Magento store views

Related

- Deploy Magento
- Magento reference
- Magento best practices
- SSH access


Magento reference

magento cli bin/magento magento file layout magento cron magento commands queue consumers magento reference

# Magento reference

Reference for running Magento 2 (Adobe Commerce) on TurboStack: where the files live, the command-line tool, and the scheduled jobs. For setup see Deploy Magento; for tuning see Magento best practices.


Magento reference

File layout

Everything lives under your system user's home directory. `~` is that home directory (for example `/var/www/prod/`). Run every `bin/magento` command from `~/public_html`.

| Path | What it is |
| --- | --- |
| `~/public_html/` | Magento project root; run the command-line tool from here |
| `~/public_html/pub/` | Web document root (the served files) |
| `~/public_html/bin/magento` | The Magento command-line tool |
| `~/public_html/app/` | Core modules, configuration (`app/etc`) and custom themes |
| `~/public_html/vendor/` | Composer dependencies |
| `~/public_html/var/` | Cache, generated code, sessions and logs |
| `~/nginx/` | Custom Nginx configuration |
| `~/.config/systemd/user/magento-consumer@.service` | Message-queue consumer service |

For ownership and permissions, see Application file layout and permissions.


Magento reference

Command-line reference

Magento ships a command-line interface (CLI) at `bin/magento`. Run `php bin/magento list` for the full list; the common commands are below.

| Command | What it does |
| --- | --- |
| `setup:upgrade` | Apply database schema and data changes from modules |
| `setup:di:compile` | Compile dependency injection code |
| `setup:static-content:deploy` | Generate static view files for the storefront |
| `deploy:mode:set {developer\|production}` | Set the deployment mode (`deploy:mode:show` reads it) |
| `cache:clean` / `cache:flush` | Clear enabled cache types / flush all cache backends |
| `cache:status` / `cache:enable` / `cache:disable` | Show or change which cache types are active |
| `indexer:reindex` | Rebuild indexers (for example catalog and search data) |
| `indexer:status` / `indexer:reset` / `indexer:set-mode` | Inspect, reset or switch schedule/realtime indexer mode |
| `config:set <path> <value>` / `config:show <path>` | Set or read a store configuration value |
| `maintenance:enable` / `maintenance:disable` / `maintenance:status` | Control maintenance mode |
| `module:status` / `module:enable` / `module:disable` | List, enable or disable a module |
| `admin:user:create` / `admin:user:unlock <user>` | Create an admin user or unlock a locked one |
| `cron:run` / `cron:install` | Run due cron jobs now / install the cron entries |
| `queue:consumers:list` / `queue:consumers:start <name>` | List message-queue consumers or start one |

> [!TIP]
> After deploying code on a production store, run `setup:upgrade --keep-generated` -> `setup:di:compile` -> `setup:static-content:deploy` -> `cache:flush`. See Magento best practices.


Magento reference

Full-page cache debugging

Magento's built-in full-page cache adds an `X-Magento-Cache-Debug` response header: `HIT` means the page was served from cache, `MISS` means it was rendered fresh. The header appears only in developer mode (`bin/magento deploy:mode:set developer`) and is suppressed in production. Check it over SSH:

```bash
curl -sI https://example.com/ | grep -i x-magento-cache
```

With Varnish in front, use the Varnish cache headers instead.

> [!NOTE]
> In production, static files are pre-generated with `setup:static-content:deploy`. You can allow on-demand generation by adding `'static_content_on_demand_in_production' => 1` to `app/etc/env.php`, but Adobe recommends against this on live stores because it adds latency on the first request for each uncached asset.


Magento reference

Cron and message-queue consumers

TurboStack installs the Magento cron entries for your system user automatically (`bin/magento cron:run`, `setup:cron:run` and the updater). These drive indexing, emails and scheduled jobs. Do not disable cron on a live store.

On a busy store, run each message-queue consumer as a persistent user system service so it restarts on failure, rather than relying on cron alone. The Magento consumer template unit and the exact `systemctl --user` commands are in Magento best practices.


Magento reference

Related

- Deploy Magento
- Magento best practices
- How to run multiple Magento store views
- Troubleshooting Magento
- Application file layout and permissions
- How to manage user system services


Troubleshooting Magento

magento troubleshooting magento logs magento error magento turbostack magento maintenance mode indexer reindex Elasticsearch connection Varnish 503

# Troubleshooting Magento

Most Magento problems have a few common causes: stale caches, invalid indexers, a stopped cron, a search-engine connection, or the site stuck in maintenance mode. This page shows where to look and how to fix the common cases. Many fixes use Magento's CLI (`bin/magento`) or the diagnostic tool `n98-magerun2`, both available from your app root.


Troubleshooting Magento

Where to find the logs

Your Magento app lives at `/var/www/<user>/public_html` (the vhost serves its `pub/` subdirectory). The most useful logs are:

| Component | Where |
| --- | --- |
| Magento application | `/var/www/<user>/public_html/var/log/` (`system.log`, `exception.log`, `debug.log`) |
| Magento cron | `var/log/magento.cron.log` and `var/log/setup.cron.log` |
| Reports / fatal errors | `/var/www/<user>/public_html/var/report/` |
| Nginx access/error | the host's web server logs (under the system user's home / `/var/log/`) |
| PHP-FPM | the PHP-FPM pool log for the app's runtime |
| Database (MySQL) | the MySQL error/slow-query log |
| Varnish | the Varnish service log |

All files under `var/log/` are rotated weekly. You can also use the host's Health tab for resource graphs and check recent deploys under History.


Troubleshooting Magento

Common issues

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| Site shows "service unavailable" / maintenance page | Maintenance mode left enabled (often after a failed deploy) | `bin/magento maintenance:status`, then `bin/magento maintenance:disable` |
| Old content, changed config or prices not showing | Stale cache | `bin/magento cache:flush` (and `cache:clean`) |
| Catalog/prices wrong, "indexer invalid" warning | Indexers out of date | `bin/magento indexer:status`, then `bin/magento indexer:reindex` |
| Storefront errors, empty search results | Elasticsearch/OpenSearch down or misconfigured | Verify the service is running on Services; re-check the search engine host and index prefix in admin |
| Emails, reindex, or scheduled jobs never run | Cron not running | Confirm the cron jobs exist for the system user and read `var/log/magento.cron.log`; run `bin/magento cron:run` manually to test |
| 503 errors from the storefront | Varnish backend (PHP/nginx) down, or wrong Varnish hosts | Check PHP-FPM/nginx are up; verify Magento's `http-cache-hosts` and Varnish backend port (8080) settings |
| White screen / blank pages, permission errors | Wrong file permissions or stale generated code | Fix ownership of `var/`, `generated/`, `pub/static`; clear with `rm -rf generated/* var/cache/*` then `setup:di:compile` |
| Admin/storefront very slow | Running in developer mode in production | `bin/magento deploy:mode:set production` |
| Indexer stuck or failing repeatedly | Corrupted indexer state | `bin/magento indexer:reset`, then `indexer:reindex`; in production set indexers to cron-driven with `indexer:set-mode schedule` |
| Missing or stale CSS/JS in the storefront | Static content not deployed | `bin/magento setup:static-content:deploy -f` (`-f` forces the deploy even in developer mode) |
| Stock quantities wrong after orders or returns | Inventory reservations inconsistent | `bin/magento inventory:reservation:list-inconsistencies`, then `inventory:reservation:create-compensations` |
| Locked out of the admin | Too many failed admin logins | `bin/magento admin:user:unlock <username>` |


Troubleshooting Magento

A troubleshooting workflow

1. **Check Health** - rule out resource exhaustion (CPU, memory, disk) and confirm the host is up.
2. **Read the relevant log** - start with `var/log/exception.log` and `var/log/system.log`, plus the nginx/PHP-FPM logs for HTTP-level errors.
3. **Check the last deploy in History** - if a recent change broke the site, revert or re-publish a known-good revision via Publishing.
4. **Verify services are running** - on Services confirm MySQL, Elasticsearch/OpenSearch, Redis, and Varnish are all up; then re-run `cache:flush` and `indexer:reindex` and re-test.


Troubleshooting Magento

Getting help

If you are still stuck, reach out via Support, or work through the platform-wide Troubleshooting guide for issues that are not Magento-specific.


Troubleshooting Magento

Related

- Deploy Magento
- Magento best practices
- Health
- Support


Medusa best practices

medusa performance medusa optimization medusa caching medusa turbostack node.js reverse proxy postgresql Redis headless commerce

# Medusa best practices

Medusa is a Node.js headless commerce platform that runs as one or more long-lived Node processes, with Nginx reverse-proxying public traffic to each one. Because there is no dedicated `app_type`, performance and stability come from how you build, run, and keep those processes running - TurboStack provides the surrounding infrastructure. This page covers what the platform configures for you and how to get the most out of it.


Medusa best practices

What TurboStack configures for you

When you deploy Medusa with the reverse-proxy pattern, the platform provides the infrastructure around your Node processes:

- **Nginx reverse proxy** - for every vhost with `proxy_enabled: true`, Nginx creates an upstream pointing at `proxy_upstream_host` (default `127.0.0.1`) on your `proxy_upstream_port`, and proxies all traffic to it. The proxy is tuned with keepalive (up to 2048 pooled connections), `proxy_http_version 1.1`, and WebSocket pass-through (`Upgrade`/`Connection` headers), so server-sent events and live admin updates work by default.
- **Transport Layer Security (TLS) termination** - `cert_type: letsencrypt` issues and auto-renews certificates per domain. Nginx terminates TLS and forwards `X-Forwarded-Proto`, `X-Forwarded-Host`, and `Ssl-Offloaded` headers so Medusa generates correct absolute URLs.
- **PostgreSQL** - the database Medusa persists all of its data in, provisioned from your `postgresql_version`.
- **Node.js runtime** - the `nodejs_version` you pin per vhost installs the Node runtime each service runs on.
- **Per-vhost separation** - each `app_name` (storefront, backend/admin) gets its own document root, Nginx vhost, access log, and upstream. Services run as independent processes on their own ports and domains.

> [!NOTE]
> TurboStack configures Nginx reverse proxy, TLS, PostgreSQL, and the Node runtime. The Medusa processes themselves - building, migrating, and keeping them running on the upstream port - are yours to manage.


Medusa best practices

Recommended optimizations

Combine Medusa's vendor guidance with the TurboStack options below. Most caching and runtime services are one toggle away on the host's Services tab.

- **Build before you serve** - always run `medusa build` (and build the storefront/admin) and start from the compiled output for production. Never run a dev server behind the proxy.
- **Run in production mode** - start each process with `NODE_ENV=production` so Medusa disables dev-only behavior and the admin is served pre-built.
- **Keep processes running with a process manager** - run each Node service under a supervisor (pm2, or a systemd unit). It must restart on crash and on reboot and keep listening on the configured `proxy_upstream_port`; if it stops, Nginx returns 502. For example, run the backend and storefront under pm2: `pm2 start "npx medusa start" --name medusa-backend` and `pm2 start "npm start" --name storefront`, then `pm2 save`. See Keep a Node.js app running for the full pm2 workflow.
- **Add Redis for events and cache** - point Medusa's modules at a Redis instance for the event bus, workflow engine, and cache. This ensures jobs and pub/sub survive restarts and scale beyond a single process. Enable Redis from Services, then set up the cache module - see Redis cache configuration.
- **Split server and worker modes** - for busy stores, run a dedicated worker process (`workerMode: "worker"`) alongside the request-serving process (`workerMode: "server"`) so background jobs do not block API responses.
- **Tune PostgreSQL connection pooling** - size the Medusa database pool to your CPU count; avoid exhausting PostgreSQL connections across server and worker processes.
- **Serve and cache static assets via a Content Delivery Network (CDN)** - place an HTTP cache/CDN in front of the storefront. This prevents the Node process from serving cacheable pages and assets on every request.
- **Optimize images and assets** - pre-build and compress storefront assets; let Next.js (or your storefront framework) emit optimized, cacheable output.


Medusa best practices

Redis cache configuration

Medusa's caching module is off by default. Enable it with a feature flag, then point the cache module at the TurboStack cache Redis instance (port 6379, no password). Give each shop its own prefix and a Time to Live (TTL) so cache data stays separate and Redis does not fill up.

Add the variables to the `.env` file in your project (`~/<project>/<shop>/`):

```bash
# Enable the caching system
MEDUSA_FF_CACHING=true

# Cache Redis instance
CACHE_REDIS_URL=redis://127.0.0.1:6379

# Optional
CACHE_TTL=28800   # 8 hours in seconds
CACHE_PREFIX=example-cache:
```

Then enable the feature flag and register the Redis cache provider in `medusa-config.ts`:

```javascript
import { defineConfig } from "@medusajs/framework/utils"

export default defineConfig({
  featureFlags: {
    caching: true,
  },

  modules: [
    {
      resolve: "@medusajs/medusa/caching",
      options: {
        providers: [
          {
            id: "caching-redis",
            resolve: "@medusajs/caching-redis",
            is_default: true,
            options: {
              redisUrl: process.env.CACHE_REDIS_URL,
              ttl: process.env.CACHE_TTL
                ? parseInt(process.env.CACHE_TTL, 10)
                : undefined,
              prefix: process.env.CACHE_PREFIX,
            },
          },
        ],
      },
    },
  ],
})
```

Medusa does not cache responses until a query opts in, so enabling the module alone does not populate Redis. A query enables caching through its `cache` option. Wire up a workflow that runs a cached query, then an API route that calls it.

Create a workflow at `~/<project>/<shop>/src/workflows/cache-products.ts`:

```javascript
import {
  createWorkflow,
  WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { useQueryGraphStep } from "@medusajs/medusa/core-flows"

export const cacheProductsWorkflow = createWorkflow(
  "cache-products",
  () => {
    const { data: products } = useQueryGraphStep({
      entity: "product",
      fields: ["id", "title"],
      options: {
        cache: {
          enable: true,
          providers: ["caching-redis"],
        },
      },
    })

    return new WorkflowResponse(products)
  }
)
```

This queries the product entity and caches the response in Redis through the query's `cache` option.

Then expose it with an API route at `~/<project>/<shop>/src/api/cache-product/route.ts`:

```javascript
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { cacheProductsWorkflow } from "../../workflows/cache-products"

export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
  const { result } = await cacheProductsWorkflow(req.scope)
    .run({})

  res.status(200).json(result)
}
```

This adds a `/cache-product` endpoint that runs the workflow and returns the cached product data.

Rebuild and restart after the change. Stop the process first, rebuild, and copy the environment file into the compiled server directory (the build produces a fresh `.medusa/server` that does not carry your `.env`):

```bash
pm2 stop <process_name>
npx medusa build
cp .env .medusa/server/.env
pm2 restart <process_name>
```

Click around the storefront or hit the `/cache-product` endpoint, then confirm keys appear under your `CACHE_PREFIX` in Redis.


Medusa best practices

Sizing and scaling

TurboStack auto-tunes resource limits from the host's size, so start with the defaults. Override the tuning variables only when you have measured evidence (slow queries, cache evictions, saturated CPU):

| Variable | Tune when |
| --- | --- |
| `postgresql` resources | The database is the bottleneck under order/catalog load |
| `redis_memory` | Redis is evicting keys used for events or cache |

Scale Node throughput by running more processes (server + worker, or multiple server instances on different ports behind Nginx) before scaling the host. See Performance tuning before changing any tuning variable, and scale up the host when a single machine can no longer keep up.


Medusa best practices

Stability

- **Back up regularly** - verify scheduled Backups cover the PostgreSQL database and any uploaded media/file storage.
- **Watch Health** - monitor CPU, memory, and disk. A crash-looping Node process or a stuck migration appears here first.
- **Run migrations on every deploy** - apply `medusa db:migrate` before starting new code so the schema matches the running version.
- **Keep versions current** - track Medusa releases and keep your pinned `nodejs_version` and `postgresql_version` on supported lines.
- **Test on a staging clone** - trial upgrades, plugins, and migrations on a copy before publishing to production.


Medusa best practices

Related

- Deploy Medusa
- Troubleshooting Medusa
- How to manage user system services
- Services
- Performance tuning


Deploy Medusa on TurboStack

medusa node.js headless commerce reverse proxy proxy_enabled postgresql nodejs_version turbostack

# Deploy Medusa on TurboStack

Medusa is a Node.js headless commerce platform. Unlike applications that have a dedicated `app_type`, Medusa is deployed with the **reverse-proxy pattern**: each Node.js service runs as its own process and Nginx proxies requests to it. This pattern applies to any Node.js or containerized application, not just Medusa, so you can use the same approach for custom Node apps, separate storefronts, and admin dashboards.


Deploy Medusa on TurboStack

Requirements

| Component | Value |
| --- | --- |
| App type | - (reverse proxy) |
| Runtime | Node.js 20+ (LTS versions only) |
| Database | PostgreSQL |
| Web server | Nginx (reverse proxy) |

> [!NOTE]
> There is no `app_type` for Medusa. You enable the reverse proxy per vhost with `proxy_enabled: true` and point it at the port your Node process listens on.


Deploy Medusa on TurboStack

Configure it

1. Set `webserver: nginx` at the host level to provide the reverse proxy.
2. Set `postgresql_version` to the PostgreSQL release Medusa should use (for example `"18"`).
3. For each service, add a vhost with a unique `app_name`, a `nodejs_version`, and `cert_type: letsencrypt`.
4. Set `proxy_enabled: true` and `proxy_upstream_port` to the port the Node process listens on. Nginx will proxy public traffic to that port.
5. Repeat for each service - for example a storefront on port 8000 and the Medusa backend/admin on port 9000.

See Applications for supported deployment patterns and Publish for how to apply your host configuration.


Deploy Medusa on TurboStack

Example configuration

```yaml
webserver: nginx
postgresql_version: "17"       # Medusa stores its data in PostgreSQL
system_users:
  - username: prod
    vhosts:
      - server_name: shop.example.com www.shop.example.com
        app_name: frontend
        nodejs_version: "24"      # Node.js runtime for the storefront
        cert_type: letsencrypt
        proxy_enabled: true        # nginx proxies to the Node process
        proxy_upstream_port: "8000"
      - server_name: dashboard.shop.example.com
        app_name: dashboard
        nodejs_version: "24"
        cert_type: letsencrypt
        proxy_enabled: true
        proxy_upstream_port: "9000"   # Medusa backend/admin
```


Deploy Medusa on TurboStack

Why these choices

- The reverse-proxy pattern (`proxy_enabled` + `proxy_upstream_port`) works for any Node.js or containerized application. You keep full control over the runtime, while Nginx handles Transport Layer Security (TLS) and routing.
- A separate vhost per `app_name` lets the storefront and the backend/admin run as independent processes on their own ports and domains.
- `nodejs_version` pins the Node.js runtime each service runs on.
- `postgresql_version` is required because Medusa persists its data in PostgreSQL.
- `cert_type: letsencrypt` issues and renews TLS certificates automatically for each domain.


Deploy Medusa on TurboStack

Related

- Technologies used: Node.js, PostgreSQL, Redis, Reverse proxy.

- Setup example - end-to-end install over SSH.
- Hosting reference - file layout, permissions & caching.
- Best practices - performance & stability.
- Troubleshooting - logs & common fixes.
- Applications overview
- Services
- Applications
- The Source (YAML) view


Medusa hosting reference

medusa reference medusa file structure medusa permissions medusa caching redis varnish varnish_enabled

# Medusa hosting reference

TurboStack is equipped to run your optimized Medusa application. TurboStack maintains the server and infrastructure, but the Medusa configuration, themes, plugins and customizations are your responsibility. This guide covers the file structure, permissions, caching and more of your Medusa application. To deploy the application, see Deploy Medusa.


Medusa hosting reference

File structure

The following table shows the directory structure of a Medusa application:

| Path | Purpose |
| --- | --- |
| `src/admin` | Holds your admin dashboard's custom widgets and UI routes. |
| `src/api` | Holds your custom API routes that are added as endpoints in your Medusa application. |
| `src/jobs` | Holds your scheduled jobs that run at specified intervals during the application's runtime. |
| `src/links` | Holds your module links that build associations between data models of different modules. |
| `src/modules` | Holds your custom modules that implement business logic. |
| `src/scripts` | Holds your custom scripts to be executed using Medusa's CLI tool. |
| `src/subscribers` | Holds your event listeners that run asynchronously whenever an event is emitted. |
| `src/workflows` | Holds your custom flows that can be executed from anywhere in your application. |
| `medusa-config.ts` | Holds your Medusa configurations, such as PostgreSQL database configuration. |
| `.medusa` | Holds types and other files generated by Medusa when running `build`; should not be modified or committed. |


Medusa hosting reference

Permissions

Medusa requires correct file permissions to operate securely and reliably. Run these commands as your own system user over SSH - you do not need root:

```bash
find ~/<Project>/<Shopname> -type f -exec chmod 644 {} \;
find ~/<Project>/<Shopname> -type d -exec chmod 755 {} \;
chown -R $USER:$USER ~/<Project>/<Shopname>
```

> [!NOTE]
> Replace `<Project>` and `<Shopname>` with your actual project and shop name.


Medusa hosting reference

Configuring Redis

Redis is enabled by default. TurboStack runs two instances on the host: port `6379` for transient cache data and port `6378` for persistent data such as sessions and queues. Access is secured by the host, so no Redis password is set. See Redis on TurboStack.


Medusa hosting reference

Varnish

The Medusa dashboard does not need any caching. We do recommend adding Varnish to your storefront. You can enable Varnish in the TurboStack GUI by adding the following line to the YAML configuration, for the `vhost` where your frontend resides:

```yaml
varnish_enabled: true
```

See Configure Varnish.


Medusa hosting reference

Clearing the cache

To clear the caches, run the following commands:

```bash
tscli varnish clear
tscli redis clear
```

`tscli redis clear` flushes the cache instance (`6379`) only; it leaves the persistent instance (`6378`) untouched, so sessions and queues survive. For finer control, see Clear the Redis cache.


Medusa hosting reference

Related

- Deploy Medusa
- Medusa best practices


Medusa setup example

medusa setup example create-medusa-app medusa storefront medusa admin medusa production pm2 postgresql

# Medusa setup example

This page walks through a full Medusa installation on TurboStack, step by
step: the Medusa backend (which includes the admin dashboard) and the optional Next.js storefront.
It is a worked example that complements Deploy Medusa on TurboStack - read that page
first for the host configuration and the reasoning behind the reverse-proxy pattern.

If you only want the backend, you can skip the storefront steps. Medusa exposes an Application
Programming Interface (API) so you can connect your own storefront later.


Medusa setup example

Before you start

Configure the host as described in Deploy Medusa on TurboStack. The example below
assumes two websites (vhosts) under one system user, each with `proxy_enabled: true` so Nginx
proxies public traffic to the Node process:

- a storefront on port `8000` (for example `shop.example.com`)
- the Medusa backend and admin on port `9000` (for example `dashboard.example.com`)

```yaml
webserver: nginx
postgresql_version: "18"
system_users:
  - username: prod
    vhosts:
      - server_name: shop.example.com www.shop.example.com
        app_name: frontend
        nodejs_version: "24"
        cert_type: letsencrypt
        proxy_enabled: true
        proxy_upstream_port: "8000"
      - server_name: dashboard.example.com
        app_name: dashboard
        nodejs_version: "24"
        cert_type: letsencrypt
        proxy_enabled: true
        proxy_upstream_port: "9000"
```

Publish the host so the two websites and PostgreSQL exist, then open an SSH
session as the system user for the steps below.

> [!NOTE]
> If you only want the backend, remove the storefront vhost.


Medusa setup example

Step 1: Create the Medusa project

Create a directory for the project in your home directory and change into it:

```bash
mkdir project
cd project
```

> [!WARNING]
> Do not place the project inside `public_html`. That directory is served publicly and would expose
> source and configuration files.

Run the Medusa installer:

```bash
npx create-medusa-app@latest <shopname>
```

Follow the interactive prompts. When asked, provide your PostgreSQL credentials:

```
? Would you like to install the Next.js Starter Storefront? Yes
? Enter your Postgres username prod
? Enter your Postgres password [hidden]
? Enter your Postgres user's database name prod
```

You find the PostgreSQL credentials in the TurboStack interface under the host's **Credentials**, or
on the server in the `~/.pgpass` file:

```bash
cat ~/.pgpass
```

The installer is finished when it prints:

```
Server is ready on port: 9000
```

Stop the test server with `Ctrl + C`. You will start it as a persistent service later.

> [!NOTE]
> If you only want the backend, answer `No` to the storefront prompt.


Medusa setup example

Step 2: Create an admin user

You need an admin account to sign in to the dashboard. Change the values to your own:

```bash
npx medusa user -e user@example.com -p <StrongPassword>
```


Medusa setup example

Step 3: Allow your dashboard domain

Medusa's admin runs on a development server that only accepts requests for hosts it knows. Add your
dashboard domain to the `allowedHosts` list in `medusa-config.ts`, found at
`~/project/<shopname>/medusa-config.ts`. Add the `admin` block:

```javascript
import { loadEnv, defineConfig } from '@medusajs/framework/utils'

loadEnv(process.env.NODE_ENV || 'development', process.cwd())

module.exports = defineConfig({
  projectConfig: {
    databaseUrl: process.env.DATABASE_URL,
    http: {
      storeCors: process.env.STORE_CORS,
      adminCors: process.env.ADMIN_CORS,
      authCors: process.env.AUTH_CORS,
      jwtSecret: process.env.JWT_SECRET || "supersecret",
      cookieSecret: process.env.COOKIE_SECRET || "supersecret",
    },
  },
  admin: {
    // Allow the host (and its subdomains) that serve the admin dashboard
    vite: (config) => {
      return {
        ...config,
        server: {
          allowedHosts: [".example.com"],
        },
      }
    },
  },
})
```


Medusa setup example

Step 4: Build and run the backend for production

The development server is not suitable for production. From the project directory
`~/project/<shopname>/`, build the backend:

```bash
npx medusa build
```

The build writes a production bundle to `.medusa/server`. Change into it and install its
dependencies:

```bash
cd .medusa/server
npm install
```

Copy the environment file from the project root into the build directory:

```bash
cp ../../.env .env
```

Start the server once to confirm it works:

```bash
npx medusa start
```

Open `https://dashboard.example.com` in your browser and sign in with the admin account you created.
When the backend loads correctly, stop it with `Ctrl + C` and start it as a persistent process so it
survives your logout and restarts on boot. A Node process manager such as PM2 does this:

```bash
pm2 start "npx medusa start" --name medusa-backend
```


Medusa setup example

Step 5: Build and run the storefront (optional)

Change into the storefront directory and install its dependencies:

```bash
cd ~/project/<shopname>-storefront
npm install
```

Build and start it once to confirm it listens on port `8000`:

```bash
npm run build
npm start
```

When it works, stop it with `Ctrl + C` and start it as a persistent process:

```bash
pm2 start "npm start" --name storefront
```

The storefront is now reachable at `https://shop.example.com`, proxied by Nginx to port `8000`.


Medusa setup example

Tip: Non-interactive install

To script the installation, pass the database connection string and the storefront flag directly, so
`create-medusa-app` does not prompt:

```bash
npx create-medusa-app@latest <shopname> \
  --db-url "postgres://<username>:<password>@localhost:5432/<dbname>" \
  --with-nextjs-starter
```

Read the credentials from `~/.pgpass` rather than copying them from the interface. If you only need
the backend, delete the generated storefront directory afterward.


Medusa setup example

Related

- Deploy Medusa on TurboStack - the host YAML and the reverse-proxy pattern.
- Medusa best practices - performance and stability.
- Troubleshooting Medusa - logs and common fixes.
- What is SSH?
- Applications overview


Troubleshooting Medusa

medusa troubleshooting medusa logs medusa error medusa turbostack 502 bad gateway node.js postgresql reverse proxy

# Troubleshooting Medusa

Most Medusa problems on TurboStack have one root cause: the Node process is not running and listening on the port Nginx proxies to. Because the platform only provides the reverse proxy, Transport Layer Security (TLS), PostgreSQL, and the Node runtime, the application process is yours to keep healthy. This page shows where to look and how to fix the common cases.


Troubleshooting Medusa

Where to find the logs

Medusa runs as your own Node process, so its application output goes wherever your process manager writes it. The platform components have fixed locations:

| Component | Where |
| --- | --- |
| Nginx access log (per vhost) | `/var/log/nginx/<user>_<app_name>.log` |
| Nginx error log | `/var/log/nginx/error.log` (proxy/502 errors land here) |
| Medusa application log | wherever your process manager directs stdout/stderr (for example the systemd journal via `journalctl -u <service>`, or PM2 logs) |
| PostgreSQL | the PostgreSQL service log on the host |
| Host metrics & service status | the host's Health tab |
| Recent deploys | History |

> [!TIP]
> A 502 is almost always logged in `/var/log/nginx/error.log` with a "connect() failed" or "upstream prematurely closed" message naming the upstream port. That tells you immediately whether Nginx reached your Node process.


Troubleshooting Medusa

Common issues

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| **502 Bad Gateway** | The Node process is not running, or not listening on `proxy_upstream_port` | Start the process; confirm it listens on the configured port (`ss -ltnp | grep <port>`); ensure `proxy_upstream_port` matches the port the app binds to |
| **502 only after a deploy / under load** | Process crashed on startup or is crash-looping | Read the application log for the stack trace; verify the build succeeded and env vars are set; run the process under a supervisor so it restarts |
| **App starts then exits immediately** | Missing or wrong environment variables (database URL, JWT/cookie secrets, Redis URL) | Set the required env vars for the process; restart after correcting them |
| **Database errors / "connection refused"** | Wrong PostgreSQL connection string, or pool exhausted | Verify the database URL, credentials, and that PostgreSQL is running; reduce pool size if connections are exhausted across server and worker |
| **"relation does not exist" / schema errors** | Migrations not run for the deployed version | Run `medusa db:migrate` before starting the new code |
| **Admin dashboard blank or 404** | The app was not built, or the wrong vhost/port serves the admin | Run `medusa build`; confirm the storefront and backend/admin use the correct separate ports and vhosts |
| **Events/jobs not firing** | Redis not configured, or no worker process running | Point Medusa at Redis and run a worker-mode process for background jobs |


Troubleshooting Medusa

A troubleshooting workflow

1. **Check Health** - confirm the host has CPU, memory, and disk headroom, and that core services are up.
2. **Read the relevant log** - for a 502, start with `/var/log/nginx/error.log`. For app crashes, read the Medusa application log (your process manager's output). For database errors, check the PostgreSQL log.
3. **Check the last deploy in History** - if a recent change broke the app, revert or re-publish the previous configuration (Publishing).
4. **Verify services are running** - confirm the Node process is alive and listening on `proxy_upstream_port`, and that PostgreSQL (and Redis, if used) are running. Restart whatever is down via your process manager or the host's Services tab.


Troubleshooting Medusa

Getting help

If you are stuck after working through the steps above, see the general Troubleshooting guide and reach out via Support. Include the failing domain, the Nginx and application log excerpts, and what changed in the last deploy.


Troubleshooting Medusa

Related

- Deploy Medusa
- Medusa best practices
- Health
- Support


Nextcloud best practices

nextcloud performance nextcloud optimization nextcloud caching nextcloud turbostack Redis file locking nextcloud cron occ large file uploads

# Nextcloud best practices

Reliable Nextcloud performance depends on the right cache layers, a tuned PHP runtime, and healthy background jobs. On TurboStack, most of this is configured when you deploy a `nextcloud` app, so your main effort is keeping caching healthy and following Nextcloud's own production guidance. This page covers what the platform configures and the optimizations to layer on top.


Nextcloud best practices

What TurboStack configures for you

When you deploy Nextcloud, TurboStack provisions a working, production-shaped stack:

- **Nginx vhost tuned for Nextcloud** - the document root is set to `public_html`. Standard Nextcloud rewrites and `location` blocks are in place: front-controller routing through `index.php`, `.well-known` redirects for CalDAV/CardDAV (calendar and contacts), and `deny` rules for `data`, `config`, `lib`, `3rdparty`, `occ`, and similar paths. Security headers are added, `X-Powered-By` is hidden, and static assets are served with long-lived `Cache-Control`.
- **PHP-FPM backend per site** - PHP requests are passed to a dedicated FastCGI pool, with `fastcgi_request_buffering off` and `HTTPS on` set so large uploads and DAV requests behave correctly. DAV is short for Distributed Authoring and Versioning, the WebDAV protocol behind CalDAV and CardDAV.
- **Redis (required)** - installed and configured in `config.php` over a unix socket (`/var/run/redis/redis.sock`). It is configured for transactional file locking (`memcache.locking = \OC\Memcache\Redis`), with APCu (`memcache.local = \OC\Memcache\APCu`) as the local memory cache.
- **config.php essentials** - `trusted_domains` is populated with your server name, FQDN, and host IP. `overwrite.cli.url`, `mysql.utf8mb4`, and the `production` updater channel are set via `occ config:system:set`.
- **Database** - a dedicated MySQL/MariaDB (or PostgreSQL) database and user are created and Nextcloud is installed against it with `occ maintenance:install`.
- **Transport Layer Security (TLS) and large uploads** - TLS is provisioned (Let's Encrypt or your own certificate). The upload ceiling is driven by `nextcloud_max_upload_size` (default `512m`), which also sets the PHP and web-server limits.
- **Hardened data permissions** - the data directory is created outside the web root, owned by the system user, with directories at `0750` and files at `0640`.


Nextcloud best practices

Recommended optimizations

- **Keep Redis healthy** - Redis is mandatory for transactional file locking and the memory cache. Confirm it is running as a service and monitor its memory so locks do not revert to the database.
- **Use system cron for background jobs** - set the background job mode to **Cron** (`occ background:job:mode cron`). A system scheduler then runs `cron.php` every 5 minutes, which is faster and more reliable than the AJAX (Asynchronous JavaScript and XML) or webcron modes (`nextcloud_background_cron`).
- **OPcache** - the PHP runtime ships with OPcache; keep it enabled (with a generous `interned_strings_buffer` and `memory_consumption`) so compiled PHP is reused across requests.
- **Add full-text search (optional)** - for large libraries, install the Full Text Search apps backed by Elasticsearch/OpenSearch so document search stays fast.
- **Raise upload limits deliberately** - increase `nextcloud_max_upload_size` (and the matching PHP `upload_max_filesize`/`post_max_size`) when users sync large files; avoid setting it far higher than needed.
- **Run "Add missing indices"** - after upgrades, run `occ db:add-missing-indices` and clear the admin overview warnings to keep queries efficient.
- **Front media with a Content Delivery Network (CDN)** - serve static assets through an HTTP cache/CDN to cut origin load and improve global latency.


Nextcloud best practices

Sizing and scaling

Defaults are auto-tuned to the host, so do not pre-emptively raise them. Override sizing variables only with measured evidence (slow queries, cache evictions, swap):

| Variable | Tune when |
| --- | --- |
| `mysql_innodb_size` | The MySQL working set no longer fits in the buffer pool |
| `redis_memory` | Redis evicts keys or locking contention appears under load |
| `elasticsearch_heap_size` | Full-text search is enabled and indexing pressures memory |

See Performance tuning for the full method: measure, change one variable, re-measure.


Nextcloud best practices

Stability

- **Back up regularly** - keep automated Backups of the database and the data directory; test a restore before you need one.
- **Watch the host** - monitor the Health tab for CPU, memory, disk, and service alerts. A full data disk takes Nextcloud offline.
- **Stay current** - keep Nextcloud, its apps and the PHP runtime on supported versions; apply upgrades through the built-in updater (pinned to the `production` channel) during quiet windows.
- **Test on a staging clone** - validate major upgrades and app changes on a copy of the host before touching production.


Nextcloud best practices

Related

- Deploy Nextcloud
- Troubleshooting Nextcloud
- Services
- Performance tuning


Deploy Nextcloud on TurboStack

deploy nextcloud nextcloud hosting nextcloud turbostack self-hosted file sync Redis file locking php 8.4 postgresql large uploads

# Deploy Nextcloud on TurboStack

Nextcloud is a self-hosted platform for file sync, sharing, and collaboration. TurboStack provisions the runtime and services, can auto-install Nextcloud, and handles large upload limits without extra configuration.


Deploy Nextcloud on TurboStack

Requirements

| Requirement | Recommended |
| --- | --- |
| App type | `nextcloud` |
| Runtime | PHP 8.4 |
| Database | MySQL 8.4 or PostgreSQL |
| Cache | Redis (**required** for transactional file locking and cache) |
| Web server | Nginx or apache2 |

> [!NOTE]
> Redis is required, not optional. Nextcloud uses it for transactional file locking, so concurrent edits stay consistent.


Deploy Nextcloud on TurboStack

Configure it

1. Open your host and go to the Applications tab.
2. Select **Add app or database** and set **App Type** to `nextcloud`.
3. Choose PHP 8.4 as the runtime and set the server name for your cloud.
4. Enable a database (MySQL or PostgreSQL) and enable Redis.
5. Publish the host to apply the configuration.


Deploy Nextcloud on TurboStack

Example configuration

```yaml
webserver: nginx
mysql_version: "8.4"          # or: postgresql_version: "17"
redis_enabled: true           # REQUIRED: transactional file locking + cache
system_users:
  - username: prod
    vhosts:
      - server_name: cloud.example.com
        app_type: nextcloud
        php_version: "8.4"
        cert_type: letsencrypt
```

> [!TIP]
> Prefer PostgreSQL? Replace `mysql_version` with `postgresql_version: "17"` to provision PostgreSQL as the Nextcloud database instead.


Deploy Nextcloud on TurboStack

Why these choices

- `app_type: nextcloud` applies the Nextcloud runtime profile and can auto-install the application.
- Redis is mandatory for transactional file locking and caching under concurrent use.
- MySQL 8.4 is a solid default; `postgresql_version` is a fully supported alternative.
- Nginx (or apache2) plus PHP 8.4 matches Nextcloud's recommended stack and large upload handling.
- Let's Encrypt provides automatic Transport Layer Security (TLS) for the cloud hostname.


Deploy Nextcloud on TurboStack

Related

- Technologies used: PHP, MySQL, Redis.

- Best practices - performance & stability.
- Troubleshooting - logs & common fixes.
- Applications overview
- Services
- Applications (host tab)
- The Source (YAML) view


Troubleshooting Nextcloud

nextcloud troubleshooting nextcloud logs nextcloud error nextcloud turbostack trusted_domains maintenance mode occ nextcloud.log

# Troubleshooting Nextcloud

When Nextcloud is not working correctly, the cause is usually one of a few things. Common causes include a hostname not in `trusted_domains`, background jobs not running, Redis or the database being unreachable, upload limits too low, or a half-finished upgrade. This page shows where to look and how to work through a problem methodically. Most fixes use the `occ` command, run as the system user from the `public_html` directory.


Troubleshooting Nextcloud

Where to find the logs

| Component | Where |
| --- | --- |
| Nextcloud application log | `data/nextcloud.log` inside the data directory (`/var/www/<user>/.../data/nextcloud.log`); also visible under **Administration settings > Logging** |
| Nginx access/error log | the host's web-server logs under the system user's home, e.g. `/var/www/<user>/logs/` |
| PHP-FPM log | the PHP-FPM pool/error log for the site's runtime |
| Database | the MySQL/MariaDB or PostgreSQL service log |
| config.php | `/var/www/<user>/.../public_html/config/config.php` - inspect with `occ config:list system`, do not hand-edit while online |

Also use the host's Health tab for service status and resource alerts, and review recent deploys in History.

> [!TIP]
> Raise the log level temporarily with `occ log:manage --level debug`, reproduce the issue, then set it back to `warning` so the log stays readable.


Troubleshooting Nextcloud

Common issues

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| "Access through untrusted domain" when opening the site | The hostname is not in `trusted_domains` | Add it: `occ config:system:set trusted_domains <n> --value=cloud.example.com`. Behind a proxy/Content Delivery Network (CDN), also set `trusted_proxies` and `overwrite.cli.url`. |
| Admin warns "last background job ran long ago" | Background jobs not running via system cron | Set cron mode: `occ background:job:mode cron` and ensure the system scheduler runs `cron.php` every 5 minutes (`nextcloud_background_cron`). |
| Site shows "maintenance mode" and stays there | Maintenance flag left on after an upgrade/backup | Turn it off: `occ maintenance:mode --off`. If an upgrade was interrupted, run `occ upgrade` then turn it off. |
| File locking errors / "files locked" | Redis unreachable, so transactional locking fails | Confirm Redis is running as a service and reachable on its socket; verify `memcache.locking` in config. As a last resort clear stale locks in the `oc_file_locks` table. |
| Large files fail to upload / time out | Upload limits or proxy buffering too low | Raise `nextcloud_max_upload_size` and the PHP `upload_max_filesize`/`post_max_size`; ensure `fastcgi_request_buffering off` and a long `fastcgi_read_timeout`. |
| Admin overview shows "missing indices" or pending migrations | Schema changes from an upgrade not applied | Run `occ db:add-missing-indices` (and `occ db:add-missing-columns`); for a stalled upgrade run `occ upgrade`. |
| 403/permission errors writing files | Data directory ownership/permissions wrong | Ensure the data dir is owned by the system user with directories `0750` and files `0640`, and lives outside the web root. |


Troubleshooting Nextcloud

A troubleshooting workflow

1. **Check Health** - confirm the host is up and not out of CPU, memory or disk; a full data disk is a common, silent cause of failures.
2. **Read the relevant log** - start with `data/nextcloud.log` (or the admin Logging view), then the Nginx error log and PHP-FPM log for the failing request.
3. **Check the last deploy** - review History; if a recent change broke the site, revert and re-publish the previous working revision.
4. **Verify services are running** - confirm PHP-FPM, the database and Redis are up on the Services tab, since Nextcloud needs all three.

> [!WARNING]
> Always run `occ` as the system user (not root) from the `public_html` directory. Running it as root corrupts file ownership and can break the installation.


Troubleshooting Nextcloud

Getting help

If you are stuck after working through the steps above, see the general platform troubleshooting guide. You can also contact Support with the relevant log excerpts and the time the issue occurred.


Troubleshooting Nextcloud

Related

- Deploy Nextcloud
- Nextcloud best practices
- Health
- Support


nopCommerce best practices

nopcommerce performance nopcommerce optimization nopcommerce caching nopcommerce turbostack dotnet microsoft sql server mssql nginx reverse proxy Redis

# nopCommerce best practices

nopCommerce is a .NET e-commerce platform that runs as its own long-lived process behind Nginx, rather than under PHP-FPM. A fast, stable store depends on the .NET service staying up, a healthy SQL Server connection, and the right caching and asset settings. TurboStack provisions the process management and reverse proxy for you; this page covers what is already configured and which settings to adjust.


nopCommerce best practices

What TurboStack configures for you

When you deploy a `nopcommerce` app, the platform sets up a managed .NET stack:

- **Managed application service** - nopCommerce runs as a systemd user service (`application.service`, or `<app_name>-application.service` when you name the app) under your system user. It launches `dotnet Nop.Web.dll` from the `application/` directory in your app root.
- **Automatic restarts** - the service is configured with `Restart=always` (`RestartSec=10s`), so the storefront restarts automatically after a crash or a host reboot.
- **Nginx reverse proxy** - a nopCommerce-specific vhost (`50main.conf`) proxies all traffic to the .NET process via a per-app upstream (`dotnet_<user>`). It uses HTTP/1.1 with keep-alive for low-latency proxying.
- **Correct proxy headers** - the vhost forwards `X-Forwarded-Host`, `X-Forwarded-For`, `X-Forwarded-Proto`, and `X-Real-IP`. The service sets `ASPNETCORE_FORWARDEDHEADERS_ENABLED=true` so nopCommerce sees the real client IP and scheme, which is required for correct HTTPS links and security.
- **Microsoft SQL Server** - nopCommerce stores its data in SQL Server (`mssql_version`); the connection lives in `App_Data/dataSettings.json` inside the application directory.
- **Environment file** - runtime settings are read from a per-app `conf/.env` file, and the .NET runtime version is pinned with `dotnet_version`.


nopCommerce best practices

Recommended optimizations

Combine nopCommerce's vendor guidance with the TurboStack options below. Most service toggles live on the host's Services tab.

- **Run in production** - publish a Release build and run with `ASPNETCORE_ENVIRONMENT=Production`. Production disables developer diagnostics and is significantly faster than Development.
- **Enable bundling and minification** - turn on JS/CSS bundling and minification in **Admin > Configuration > Settings > General settings** to cut request count and payload size.
- **Use a distributed cache for scale** - for multi-instance or memory-pressured stores, point nopCommerce at Redis in `appsettings.json` instead of the default in-memory cache so cache state survives restarts.
- **Optimize images and assets** - enable image resizing/caching, serve optimized (WebP) media, and keep `wwwroot` assets cache-friendly.
- **Front static content with a Content Delivery Network (CDN)** - offload `/content`, `/images`, and theme assets to a CDN to reduce origin load and improve global latency.
- **Keep the service warm** - nopCommerce has a cold start; the always-on service and automatic restarts already keep it warm, so avoid unnecessary restarts during peak hours.
- **Let scheduled tasks run** - nopCommerce drives its own schedule tasks (queued emails, keep-alive, cleanup) from within the running app. Keep the service up and review **Admin > System > Schedule Tasks**.

> [!NOTE]
> Varnish and PHP OPcache do not apply to nopCommerce - it is a .NET application with its own internal caching, not a PHP app. Use nopCommerce's built-in caching (optionally backed by Redis) instead.


nopCommerce best practices

Sizing and scaling

TurboStack auto-tunes resource limits from the host's size, so start with the defaults. Override tuning variables only with measured evidence (slow queries, high CPU, memory pressure):

| Variable | Tune when |
| --- | --- |
| `redis_memory` | You enable Redis as the nopCommerce distributed cache and it evicts keys under load |
| SQL Server memory | The database is the bottleneck on a large catalog or high order volume |

Because nopCommerce uses SQL Server rather than MySQL/Elasticsearch, the MySQL and search-engine tuning variables do not apply. See Performance tuning before changing anything. When a single host can no longer handle the load, scale it up before splitting services.


nopCommerce best practices

Stability

- **Back up regularly** - verify scheduled Backups cover the SQL Server database and the application's `App_Data/` directory.
- **Watch Health** - monitor CPU, memory, and disk. Confirm the application service stays running.
- **Keep versions current** - track nopCommerce releases and security patches, and keep your pinned `dotnet_version` and `mssql_version` on supported releases.
- **Test on a staging clone** - always trial upgrades, plugins, and theme changes on a copy before publishing to production, since plugins recompile against the running runtime.


nopCommerce best practices

Related

- Deploy nopCommerce
- Troubleshooting nopCommerce
- How to manage user system services
- Services
- Performance tuning


Deploy nopCommerce on TurboStack

nopcommerce dotnet .net microsoft sql server mssql nginx reverse proxy app_type nopcommerce turbostack

# Deploy nopCommerce on TurboStack

nopCommerce is an open-source e-commerce platform built on .NET. TurboStack runs nopCommerce as its own service behind an Nginx reverse proxy and connects it to Microsoft SQL Server. You only need to declare the application in your host YAML.


Deploy nopCommerce on TurboStack

Requirements

| Component | Value |
| --- | --- |
| App type | `nopcommerce` |
| Runtime | .NET |
| Database | Microsoft SQL Server |
| Web server | Nginx (reverse proxy) |

> [!NOTE]
> nopCommerce uses Microsoft SQL Server, configured with `mssql_version`, rather than PostgreSQL or MySQL.


Deploy nopCommerce on TurboStack

Configure it

1. Set `webserver: nginx` at the host level so TurboStack provisions the reverse proxy.
2. Set `mssql_version` to the Microsoft SQL Server release nopCommerce should use (for example `"2022"`).
3. Add a system user, then define a vhost with `app_type: nopcommerce`. TurboStack starts the service and proxies traffic to it.
4. Set `dotnet_version` to pin the .NET runtime (for example `"8.0"`).
5. Request a certificate with `cert_type: letsencrypt` for automatic HTTPS.

See Applications for the full list of supported app types and Publish for how to apply your host configuration.


Deploy nopCommerce on TurboStack

Example configuration

```yaml
webserver: nginx
mssql_version: "2022"          # nopCommerce uses Microsoft SQL Server
system_users:
  - username: prod
    vhosts:
      - server_name: shop.example.com
        app_type: nopcommerce
        dotnet_version: "8.0"   # .NET runtime version
        cert_type: letsencrypt
```


Deploy nopCommerce on TurboStack

Why these choices

- `app_type: nopcommerce` tells TurboStack to run the nopCommerce service and configure the Nginx reverse proxy for it.
- `mssql_version` is required because nopCommerce stores its data in Microsoft SQL Server.
- `dotnet_version` pins the .NET runtime the application runs on.
- `webserver: nginx` provides the reverse proxy layer that fronts the .NET process.
- `cert_type: letsencrypt` issues and renews Transport Layer Security (TLS) certificates without manual steps.


Deploy nopCommerce on TurboStack

Related

- Technologies used: .NET, Microsoft SQL Server.

- Best practices - performance & stability.
- Troubleshooting - logs & common fixes.
- Applications overview
- Services
- Applications
- The Source (YAML) view


Troubleshooting nopCommerce

nopcommerce troubleshooting nopcommerce logs nopcommerce error nopcommerce turbostack nginx 502 dotnet service mssql connection datasettings.json

# Troubleshooting nopCommerce

nopCommerce runs as a .NET service behind nginx. Most problems fall into one of three categories: the application service is not running, Nginx cannot reach it (502), or it cannot connect to SQL Server. This page shows where the logs are and how to work through the common failures.