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
Clone the repository
git clone https://github.com/PropOps-Technologies-Ltd/PropOps-Web.git
cd PropOps-Web
Start the containers
The first run takes a few minutes as Docker builds the PHP/Apache image, pulls MariaDB, and initialises everything. Watch the progress:Wait until you see [entrypoint] Directory and permissions check complete. and Apache starts accepting connections. Verify all containers are running
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
Log in
Open http://your-server-ip:8080 in your browser. Sign in with the default admin credentials:| Field | Value |
|---|
| Email | web@propops.app |
| Password | PropOps2026! |
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:
| Key | Purpose |
|---|
PII_ENCRYPTION_KEY | AES encryption key for personally identifiable information (names, emails, phone numbers) |
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.
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:
| 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 |
feedback | User feedback attachments |
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:
| Field | Default value |
|---|
| Email | web@propops.app |
| Password | PropOps2026! |
| Role | SysOps (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:
| Service | Image | Port | Purpose |
|---|
web | Built from Dockerfile | 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 |
Do not expose port 3306 (MariaDB) or 8081 (phpMyAdmin) to the internet in production. See Configuration for firewall and reverse proxy setup.