> ## Documentation Index
> Fetch the complete documentation index at: https://help.propops.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Docker Setup

> Install PropOps using Docker Compose — start the containers and have a working system in minutes.

PropOps Web uses Docker Compose to bring up the web server, MariaDB database, and phpMyAdmin in a single command. The first run automatically creates your environment file, generates encryption keys, sets up the database, and seeds a default admin account.

<Info>
  PropOps Web is distributed as a Docker image. You will receive a `docker-compose.yml` file as part of your licence. Contact [hello@propops.app](mailto:hello@propops.app) if you have not received yours.
</Info>

***

## Installation

<Tabs>
  <Tab title="Plesk (Recommended)">
    If your server uses [Plesk](https://www.plesk.com/), you can install PropOps through the Plesk Docker Manager — the easiest way to get up and running.

    ### Prerequisites

    <Steps>
      <Step title="Install the Docker extension">
        In Plesk, go to **Extensions** → **Extensions Catalog** and search for **Docker**. Click **Install** (or **Open** if already installed).

        <Note>
          The Docker extension requires Plesk Obsidian 18.0+ and a server with KVM or bare-metal virtualisation. It is not available on shared hosting plans.
        </Note>
      </Step>
    </Steps>

    ### Install PropOps

    <Steps>
      <Step title="Upload docker-compose.yml">
        Using Plesk's **File Manager** (or SFTP), create a directory on your server — for example `/opt/propops/` — and upload the `docker-compose.yml` file provided by PropOps.
      </Step>

      <Step title="Start the containers">
        In Plesk, open a terminal from the Docker extension or use **Tools & Settings → SSH Terminal**:

        ```bash theme={null}
        cd /opt/propops
        docker compose up -d
        ```

        The containers will start and appear in Plesk's Docker Manager, where you can monitor their status, view logs, and restart them.
      </Step>

      <Step title="Get your admin credentials">
        View the web container logs in Plesk's Docker interface, or run:

        ```bash theme={null}
        docker compose logs web | grep "seed-sysops"
        ```

        You will see output like:

        ```
        [seed-sysops] │  Email:    web@propops.app
        [seed-sysops] │  Password: aBcD3fGh!jKmN5pQrStV
        ```

        <Warning>
          The password is only displayed once. Copy it now and store it securely. Change it immediately after your first login.
        </Warning>
      </Step>
    </Steps>

    ### Set up a domain proxy in Plesk

    To serve PropOps through your domain with HTTPS:

    <Steps>
      <Step title="Add or select a domain">
        In Plesk, go to **Websites & Domains** and select the domain you want to use for PropOps (e.g. `propops.yourcompany.com`).
      </Step>

      <Step title="Configure Apache & Nginx proxy">
        Go to **Apache & nginx Settings** for the domain and add the following under **Additional nginx directives**:

        ```nginx theme={null}
        location / {
            proxy_pass http://127.0.0.1:8080;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_read_timeout 120s;
            client_max_body_size 50M;
        }
        ```
      </Step>

      <Step title="Enable SSL/TLS">
        Go to **SSL/TLS Certificates** for the domain and issue a free Let's Encrypt certificate. Plesk handles renewal automatically.
      </Step>
    </Steps>

    PropOps Web is now accessible at `https://propops.yourcompany.com`.

    <Tip>
      You can manage the containers directly from the Plesk Docker Manager — start, stop, restart, and view logs without using the command line.
    </Tip>
  </Tab>

  <Tab title="Command Line">
    For servers without Plesk, or if you prefer the command line.

    <Steps>
      <Step title="Create a project directory">
        Create a directory on your server and place the `docker-compose.yml` file provided by PropOps inside it:

        ```bash theme={null}
        mkdir propops && cd propops
        ```

        Copy or download the `docker-compose.yml` into this directory.
      </Step>

      <Step title="Start the containers">
        ```bash theme={null}
        docker compose up -d
        ```

        The first run pulls the PropOps image, MariaDB, and phpMyAdmin, then initialises everything. Watch the progress:

        ```bash theme={null}
        docker compose logs -f
        ```

        Wait until you see `[entrypoint] Directory and permissions check complete.` and Apache starts accepting connections.
      </Step>

      <Step title="Verify all containers are running">
        ```bash theme={null}
        docker compose ps
        ```

        Expected output:

        ```
        NAME               IMAGE                                          STATUS         PORTS
        propops-web-1      ghcr.io/propops-technologies-ltd/propops-web   running        0.0.0.0:8080->80/tcp
        propops-db-1       mariadb:10.11                                  running        3306/tcp
        propops-phpmyadmin phpmyadmin                                     running        0.0.0.0:8081->80/tcp
        ```
      </Step>

      <Step title="Get your admin credentials">
        The entrypoint seeds a default **SysOps** admin account on first boot. The password is **randomly generated** and printed once in the container logs.

        Find it by running:

        ```bash theme={null}
        docker compose logs web | grep "seed-sysops"
        ```

        You will see output like:

        ```
        [seed-sysops] │  Email:    web@propops.app
        [seed-sysops] │  Password: aBcD3fGh!jKmN5pQrStV
        ```

        <Warning>
          The password is only displayed once. Copy it now and store it securely. Change it immediately after your first login.
        </Warning>
      </Step>

      <Step title="Log in">
        Open `http://your-server-ip:8080` in your browser and sign in with the email and password from the log output.
      </Step>
    </Steps>
  </Tab>
</Tabs>

That's it. PropOps Web is running.

***

## What happens on first boot

When you run `docker compose up` for the first time, the entrypoint script automatically performs the following setup:

### 1. Environment file created

Your `.env` configuration file is created from `.env.example` and stored in `docker-data/config/.env`. This file persists across container rebuilds — updating the application code does not overwrite your configuration.

### 2. Encryption keys generated

Two cryptographic keys are auto-generated and written into your `.env`:

| Key                  | Purpose                                                                                                                                                       |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PII_ENCRYPTION_KEY` | Encryption key for personally identifiable information in the database (names, emails, phone numbers) and job files (documents, photos, videos, GDPR reports) |
| `PII_HMAC_KEY`       | HMAC key for searchable encrypted field hashes                                                                                                                |

Both are 64-character hex strings (32 random bytes). They are generated once and must not be changed after data has been encrypted.

<Warning>
  Back up your `docker-data/config/.env` file securely. If you lose these keys, encrypted PII data in the database and encrypted job files cannot be recovered.
</Warning>

### 3. Database initialised

MariaDB applies the schema and all migrations automatically from the SQL files mounted into `/docker-entrypoint-initdb.d/`. This only happens on the very first boot when the database is empty.

### 4. Upload directories created

All required subdirectories under `uploads/` are created with correct ownership (`www-data`) and permissions:

| Directory                 | Purpose                            |
| ------------------------- | ---------------------------------- |
| `avatars`                 | User profile photos                |
| `branches`                | Branch logos and assets            |
| `cache/thumbnails`        | Generated image thumbnails         |
| `case-notes`              | Job case note attachments          |
| `certifications`          | Contractor certification documents |
| `dashboard-banners`       | Custom dashboard banner images     |
| `gdpr`                    | GDPR data export reports           |
| `jobs`                    | Job photos and documents           |
| `notices/headers`         | Notice board header images         |
| `temp/chunks`             | Temporary file upload chunks       |
| `thumbnails/avatars`      | Resized avatar thumbnails          |
| `thumbnails/branch-logos` | Resized branch logo thumbnails     |

### 5. Security rules applied

`.htaccess` files are placed into sensitive upload directories to deny direct web access. Files in these directories are only accessible through authenticated API endpoints:

* `uploads/case-notes/` — denied
* `uploads/certifications/` — denied
* `uploads/gdpr/` — denied
* `extensions/` — denied
* `docker-data/config/` — denied

### 6. Admin account seeded

A default **SysOps** administrator account is created with a **randomly generated 20-character password**:

| Field        | Default value                                       |
| ------------ | --------------------------------------------------- |
| **Email**    | `web@propops.app`                                   |
| **Password** | Randomly generated (printed once in container logs) |
| **Role**     | SysOps (full system access)                         |

The seeder grants all API and page permissions to the SysOps role. If a SysOps user already exists, seeding is skipped.

<Warning>
  The generated password is only displayed once during the first boot. Retrieve it from the container logs:

  ```bash theme={null}
  docker compose logs web | grep "seed-sysops"
  ```

  Store it securely and change it immediately after logging in.
</Warning>

You can customise the default credentials by setting environment variables before the first boot:

```bash theme={null}
# In docker-compose.yml under web > environment, or in your .env
SEED_SYSOPS_EMAIL=admin@yourcompany.com
SEED_SYSOPS_PASSWORD=YourChosenPassword!    # Optional — overrides random generation
```

Set `SEED_SYSOPS=false` to disable automatic user creation entirely.

***

## Directory structure

After the first boot, your project directory will contain a `docker-data/` folder with all persistent data:

```
propops/
├── docker-compose.yml     # Provided by PropOps
├── docker-data/           # All persistent data (created on first boot)
│   ├── config/
│   │   ├── .env           # Your environment configuration
│   │   └── .htaccess      # Denies direct web access
│   ├── uploads/           # User-uploaded files
│   │   ├── avatars/
│   │   ├── branches/
│   │   ├── case-notes/
│   │   │   └── .htaccess  # Denies direct access
│   │   ├── certifications/
│   │   │   └── .htaccess  # Denies direct access
│   │   ├── gdpr/
│   │   │   └── .htaccess  # Denies direct access
│   │   └── ...            # Other upload subdirectories
│   ├── extensions/        # Platform extensions
│   │   └── .htaccess      # Denies direct access
│   ├── mysql/             # MariaDB database files
│   └── mysql-logs/        # Database query and error logs
```

<Info>
  The `docker-data/` directory contains your database, uploads, and secrets — all specific to your installation. Back it up regularly.
</Info>

### Data persistence

All data in `docker-data/` is stored on your host filesystem. This means:

* **Container rebuilds** do not affect your data — updating the PropOps image does not touch uploads, database, or configuration.
* **Image updates** do not overwrite your `.env` or uploaded files.
* **Backing up** is straightforward — copy the `docker-data/` directory to capture everything.

***

## Services

Docker Compose starts three services:

| Service      | Image                                          | Port          | Purpose                                    |
| ------------ | ---------------------------------------------- | ------------- | ------------------------------------------ |
| `web`        | `ghcr.io/propops-technologies-ltd/propops-web` | `8080` → `80` | PHP 8.5 / Apache — the PropOps application |
| `db`         | `mariadb:10.11`                                | `3306`        | MariaDB database server                    |
| `phpmyadmin` | `phpmyadmin/phpmyadmin`                        | `8081` → `80` | Web-based database management              |

<Warning>
  Do not expose port `3306` (MariaDB) or `8081` (phpMyAdmin) to the internet in production. See [Configuration](/installation/configuration) for firewall and reverse proxy setup.
</Warning>
