Skip to main content
PropOps 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.

Installation

1

Clone the repository

git clone https://github.com/PropOps-Technologies-Ltd/PropOps-Web.git
cd PropOps-Web
2

Start the containers

docker compose up -d
The first run takes a few minutes as Docker builds the PHP/Apache image, pulls MariaDB, and initialises everything. Watch the progress:
docker compose logs -f
Wait until you see [entrypoint] Directory and permissions check complete. and Apache starts accepting connections.
3

Verify all containers are running

docker compose ps
Expected output:
NAME               IMAGE               STATUS         PORTS
propops-web-1      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
4

Log in

Open http://your-server-ip:8080 in your browser. Sign in with the default admin credentials:
FieldValue
Emailweb@propops.app
PasswordPropOps2026!
Change the default password immediately after your first login. Go to your profile settings and set a strong, unique password.
That’s it. PropOps 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:
KeyPurpose
PII_ENCRYPTION_KEYAES encryption key for personally identifiable information (names, emails, phone numbers)
PII_HMAC_KEYHMAC 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.
Back up your docker-data/config/.env file securely. If you lose these keys, encrypted PII data in the database cannot be recovered.

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:
DirectoryPurpose
avatarsUser profile photos
branchesBranch logos and assets
cache/thumbnailsGenerated image thumbnails
case-notesJob case note attachments
certificationsContractor certification documents
dashboard-bannersCustom dashboard banner images
feedbackUser feedback attachments
gdprGDPR data export reports
jobsJob photos and documents
notices/headersNotice board header images
temp/chunksTemporary file upload chunks
thumbnails/avatarsResized avatar thumbnails
thumbnails/branch-logosResized 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:
FieldDefault value
Emailweb@propops.app
PasswordPropOps2026!
RoleSysOps (full system access)
The seeder also grants all API and page permissions to the SysOps role. If a SysOps user already exists, seeding is skipped. You can customise the default credentials by setting environment variables before the first boot:
# In docker-compose.yml under web > environment, or in your .env
SEED_SYSOPS_EMAIL=admin@yourcompany.com
SEED_SYSOPS_PASSWORD=YourStrongPassword!
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-Web/
├── docker-data/           # All persistent data (host-mounted)
│   ├── 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
├── docker-compose.yml
├── Dockerfile
└── ...                    # Application source code
The docker-data/ directory is listed in .gitignore and is never committed to version control. It contains your database, uploads, and secrets — all specific to your installation.

Data persistence

All data in docker-data/ is stored on your host filesystem. This means:
  • Container rebuilds do not affect your data — docker compose up -d --build updates the application code without touching uploads, database, or configuration.
  • Application updates (via git pull) 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:
ServiceImagePortPurpose
webBuilt from Dockerfile808080PHP 8.5 / Apache — the PropOps application
dbmariadb:10.113306MariaDB database server
phpmyadminphpmyadmin/phpmyadmin808180Web-based database management
Do not expose port 3306 (MariaDB) or 8081 (phpMyAdmin) to the internet in production. See Configuration for firewall and reverse proxy setup.