# Architecture

> The services, volumes, ports and data stores that make up a deployed Enterprise instance.

> For the complete documentation index, see [llms.txt](https://helpdesk.orangescrum.com/llms.txt).

Source: https://helpdesk.orangescrum.com/guide/self-hosted/get-started/architecture

---
Knowing what runs where is what makes backup, migration and troubleshooting
straightforward rather than guesswork.

## The container stack

A Docker deployment defines four services:

| Service | Purpose | Port |
| --- | --- | --- |
| `orangescrum-app` | The application | 8080 |
| `orangescrum-postgresdb` | Main PostgreSQL database | 5432 (internal) |
| `orangescrum-reports` | Advanced Dashboard (Superset) | 8088 |
| `orangescrum-reports-db` | Superset's own PostgreSQL database | internal |

> **Two databases, not one**
>
> Reporting runs on Superset, which keeps its own metadata database separate
> from the application's. Both must be backed up — a backup covering only the
> main database restores an instance with no dashboards.

The reports service depends on its database being **healthy** before it starts,
so on a cold boot expect it to come up last. Allow a minute or two before
concluding something is broken.

## Persistent volumes

Four named volumes hold everything that must survive a container rebuild:

| Volume | Mounted at | Contents |
| --- | --- | --- |
| `orangescrum-app-files` | `/var/www/html/webroot/files` | Uploaded files and attachments |
| `orangescrum-app-config` | `/var/www/html/config` | Application configuration |
| `orangescrum-postgres-data` | `/var/lib/postgresql/data` | Main application database |
| `orangescrum-reports-data` | `/var/lib/postgresql/data` | Superset reports database |

> **Volume names are prefixed by the compose project**
>
> On disk they appear as `orangescrum-ee_orangescrum-app-files` and so on. Always
> confirm with `docker volume ls` before writing backup or restore commands —
> guessing the prefix is the most common cause of a restore that silently
> populates nothing.

## Data outside Docker

Not everything lives in a volume:

| Item | Typical location | Why it matters |
| --- | --- | --- |
| Application data root | `/data` (or your configured path) | Application files on the VM host |
| Host config | `/etc/app/` or the app root | Environment-specific settings |
| Cron jobs and scripts | `/etc/cron.d/` or the app folder | Scheduled digests and reminders |
| Compose definition | `docker-compose.yml` | The stack itself |
| Reports image build | `osreports.Dockerfile` | Custom Superset build |
| Secrets | `./secrets/` | Plaintext service passwords |

### The secrets directory

Four files hold credentials the services read at startup:

| File | Used by |
| --- | --- |
| `secrets/postgres_password.txt` | Main database |
| `secrets/reports_admin_user.txt` | Reports service |
| `secrets/reports_admin_email.txt` | Reports service |
| `secrets/reports_admin_password.txt` | Reports service |

> **These are plaintext credentials**
>
> Anything that backs up the secrets directory produces an archive containing
> live passwords. Store those archives encrypted, restrict who can read them, and
> never put the directory in version control.

## Traditional deployment

Without containers the shape is the same, just managed by the host:

- **PHP-FPM behind Apache or Nginx**, document root at `webroot/`
- **PostgreSQL** as a system service
- Attachments under `webroot/files`, configuration in `config/`
- Cron entries for scheduled jobs

Everything above the document root must be unreachable over HTTP.

## Background jobs

Some behaviour depends on scheduled work rather than the request cycle:

- **Daily Catch-Up**: The record is written immediately; the email fires from a background job.

- **Task reminders**: Stored against the task, delivered by a scheduled job at the chosen time.

> **If digests never arrive, check the scheduler first**
>
> The data being present in the database does not mean delivery ran. Confirm the
> cron entries survived your install or migration — they live outside Docker and
> are easy to lose.

## Request path

```
Browser
  → Load balancer / reverse proxy   (TLS terminates here)
    → orangescrum-app  :8080
      → orangescrum-postgresdb  :5432
      → orangescrum-reports     :8088  (Advanced Dashboard)
        → orangescrum-reports-db
```

TLS terminates at the proxy; the application itself listens on plain HTTP inside
the network. That is why `443` is what you expose and `8080` is what you do not.

- [Deployment options](https://helpdesk.orangescrum.com/guide/self-hosted/install/deployment-options): Docker or traditional — how to choose.

- [Backup](https://helpdesk.orangescrum.com/guide/self-hosted/operate/backup): Everything on this page, and how to capture it.
