# Databasement Documentation > Simple and powerful database backup management — self-hosting and user documentation for Databasement. This file contains all documentation content in a single document following the llmstxt.org standard. ## Introduction Welcome to the **Self-Hosting** section of the **Databasement documentation**! Databasement is a web application for managing database server backups. It allows you to register database servers (MySQL, PostgreSQL, MariaDB, Microsoft SQL Server, MongoDB, SQLite, Firebird, Redis/Valkey), test connections, schedule automated backups, and restore snapshots to any registered server. ## Getting Started We provide guides to deploy Databasement using: - [**Docker**](docker) - Single container deployment (recommended for most users) - [**Docker Compose**](docker-compose) - Multi-container setup with external database - [**Kubernetes + Helm**](kubernetes-helm) - For Kubernetes clusters - [**Native Ubuntu**](native-ubuntu) - Traditional installation without Docker See [Versioning](versioning) for available Docker image tags and Helm chart versions. ## Requirements Databasement runs in a single container that includes: - FrankenPHP web server - Queue worker for async backup/restore jobs - Scheduler for automated backups The only external requirement is a database for the application itself: - **SQLite** (simplest, built into the container) - **MySQL/MariaDB** or **PostgreSQL** (recommended for production) ### Supported Application Database Versions | Engine | Minimum Version | Recommended | |------------|-----------------|-------------| | SQLite | 3.26+ | Latest | | MySQL | 5.7+ | 8.x | | MariaDB | 10.3+ | 11.x | | PostgreSQL | 10.0+ | 16+ | These are the databases Databasement uses to store its own configuration, users, and backup metadata -- not the databases you back up (see [Supported Versions](../user-guide/database-servers#supported-versions) for that). ## Quick Start The fastest way to try Databasement: ```bash docker run -d \ --name databasement \ -p 2226:2226 \ -e DB_CONNECTION=sqlite \ -e DB_DATABASE=/data/database.sqlite \ -e ENABLE_QUEUE_WORKER=true \ -v ./databasement-data:/data \ davidcrty/databasement:1 ``` Then open http://localhost:2226 in your browser and create your first admin account. :::note This quick start uses SQLite for the application database. For production deployments, see the [Docker guide](docker) for recommended configurations. ::: ## Support If you encounter issues with self-hosting, please open an issue on [GitHub](https://github.com/David-Crty/databasement/issues). --- ## Docker Compose This guide will help you deploy Databasement using Docker Compose. This method is ideal when you want to run Databasement alongside its own dedicated database container. ## Prerequisites - [Docker](https://docs.docker.com/engine/install/) and [Docker Compose](https://docs.docker.com/compose/install/) - A [supported application database](intro#supported-application-database-versions) (SQLite, MySQL, MariaDB, or PostgreSQL) ## Quick Start ### 1. Create Project Directory ```bash mkdir databasement && cd databasement ``` ### 2. Generate Application Key ```bash docker run --rm davidcrty/databasement:1 php artisan key:generate --show ``` Save this key for the next step. ### 3. Create Environment File Create a `.env` file with your configuration. This file is shared between the `app` and `worker` services to ensure consistent settings. #### SQLite ```bash title=".env" APP_URL=http://localhost:2226 APP_KEY=base64:your-generated-key-here # Database (SQLite) DB_CONNECTION=sqlite DB_DATABASE=/data/database.sqlite ``` #### MySQL ```bash title=".env" APP_URL=http://localhost:2226 APP_KEY=base64:your-generated-key-here # Database (MySQL) DB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE=databasement DB_USERNAME=databasement DB_PASSWORD=your-secure-password ``` #### PostgreSQL ```bash title=".env" APP_URL=http://localhost:2226 APP_KEY=base64:your-generated-key-here # Database (PostgreSQL) DB_CONNECTION=pgsql DB_HOST=postgres DB_PORT=5432 DB_DATABASE=databasement DB_USERNAME=databasement DB_PASSWORD=your-secure-password ``` :::tip S3 Storage To store backups in AWS S3 or S3-compatible storage (MinIO, DigitalOcean Spaces, etc.), see the [S3 Storage Configuration](./configuration/backup#s3-storage) section. ::: ### 4. Create docker-compose.yml #### SQLite ```yaml title="docker-compose.yml" services: app: image: davidcrty/databasement:1 container_name: databasement restart: unless-stopped ports: - "2226:2226" env_file: .env volumes: - ./data:/data healthcheck: test: ["CMD", "curl", "-f", "http://localhost:2226/health"] interval: 10s timeout: 5s retries: 5 worker: image: davidcrty/databasement:1 container_name: databasement-worker restart: unless-stopped command: sh -c "php artisan db:wait --check-migrations && php artisan queue:work --queue=backups,default --tries=3 --timeout=3600 --sleep=3 --max-jobs=1000" env_file: .env volumes: - ./data:/data depends_on: app: condition: service_healthy ``` #### MySQL ```yaml title="docker-compose.yml" services: app: image: davidcrty/databasement:1 container_name: databasement restart: unless-stopped ports: - "2226:2226" env_file: .env volumes: - ./data:/data depends_on: mysql: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:2226/health"] interval: 10s timeout: 5s retries: 5 worker: image: davidcrty/databasement:1 container_name: databasement-worker restart: unless-stopped command: sh -c "php artisan db:wait --check-migrations && php artisan queue:work --queue=backups,default --tries=3 --timeout=3600 --sleep=3 --max-jobs=1000" env_file: .env volumes: - ./data:/data depends_on: app: condition: service_healthy mysql: condition: service_healthy mysql: image: mysql:8.0 container_name: databasement-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: your-root-password MYSQL_DATABASE: databasement MYSQL_USER: databasement MYSQL_PASSWORD: your-secure-password volumes: - ./mysql-data:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 ``` #### PostgreSQL ```yaml title="docker-compose.yml" services: app: image: davidcrty/databasement:1 container_name: databasement restart: unless-stopped ports: - "2226:2226" env_file: .env volumes: - ./data:/data depends_on: postgres: condition: service_healthy healthcheck: test: ["CMD", "curl", "-f", "http://localhost:2226/health"] interval: 10s timeout: 5s retries: 5 worker: image: davidcrty/databasement:1 container_name: databasement-worker restart: unless-stopped command: sh -c "php artisan db:wait --check-migrations && php artisan queue:work --queue=backups,default --tries=3 --timeout=3600 --sleep=3 --max-jobs=1000" env_file: .env volumes: - ./data:/data depends_on: app: condition: service_healthy postgres: condition: service_healthy postgres: image: postgres:16 container_name: databasement-postgres restart: unless-stopped environment: POSTGRES_DB: databasement POSTGRES_USER: databasement POSTGRES_PASSWORD: your-secure-password volumes: - ./postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U databasement -d databasement"] interval: 10s timeout: 5s retries: 5 ``` :::tip Pin a version See [Versioning](versioning) for available tags. ::: :::tip Remember to restart both the `app` and `worker` services whenever you change the `.env` file: `docker compose restart app worker` ::: :::tip The `worker` service runs the Laravel queue worker as a separate container. This provides better stability and allows independent restarts without affecting the web application. The worker processes backup and restore jobs from the queue. ::: ### 5. Start the Services ```bash docker compose up -d ``` ### 6. Verify the Setup Wait for all services to be healthy, then verify the application is running: ```bash # Check service status docker compose ps # Verify health endpoint curl http://localhost:2226/health ``` ### 7. Access the Application Open http://localhost:2226 in your browser. :::note To expose your Databasement instance with HTTPS, you can use Traefik as a reverse proxy. For detailed instructions on how to configure Traefik with Docker, please refer to the [official Traefik documentation](https://doc.traefik.io/traefik/expose/docker/). ::: ## Custom User ID (PUID/PGID) By default, the application runs as PUID/PGID `1000`. You can customize this using the `PUID` and `PGID` environment variables: ```yaml title="docker-compose.yml" services: app: image: davidcrty/databasement:1 environment: PUID: 1001 PGID: 1001 # ... rest of config worker: image: davidcrty/databasement:1 environment: PUID: 1001 PGID: 1001 # ... rest of config ``` :::tip Find your user's PUID/PGID with `id username`. The container will automatically set the correct permissions on `/data` for the specified PUID/PGID. ::: ### Rootless Containers For rootless Docker or Podman environments, use the `user` directive. When using this method, the container runs entirely as the specified user and skips the automatic permission fix: ```yaml services: app: image: davidcrty/databasement:1 user: "1010:1010" # ... rest of config worker: image: davidcrty/databasement:1 user: "1010:1010" # ... rest of config ``` :::note When using `user`, you must manually set `/data` directory volume permissions before starting the container since the automatic permission fix requires root: `sudo chown 1010:1010 /path/to/databasement/data` ::: ## Updating Update the image tag in your `docker-compose.yml`, then pull and recreate. Migrations run automatically on startup. ```bash docker compose pull docker compose up -d ``` See [Versioning](versioning) for available versions. ## Troubleshooting ### Database connection errors ``` SQLSTATE[HY000] [2002] No such file or directory # or SQLSTATE[HY000] [2002] Connection refused ``` **Cause:** The database container isn't ready yet, or the port/host configuration is incorrect. **Solution:** 1. Check if the database container is healthy: `docker compose ps` 2. Verify `DB_PORT` matches your database (MySQL: `3306`, PostgreSQL: `5432`) 3. Ensure `DB_HOST` matches your Docker Compose service name exactly (e.g. `mysql` or `postgres`) ### Container keeps restarting **Cause:** Application error during startup. **Solution:** Check the logs to identify the issue: ```bash docker compose logs -f app docker compose logs -f worker ``` ### Permission denied on /data **Cause:** The container user doesn't have write access to the mounted volume. **Solution:** Either: 1. Set `PUID`/`PGID` to match your host user (see [Custom User ID](#custom-user-id-puidpgid)) 2. Or fix permissions manually: `sudo chown -R 1000:1000 ./data` (replace `1000` with your PUID/PGID) ### Database tables are empty after startup **Cause:** This is expected on first run. Migrations run automatically when the app starts. **Solution:** Check if migrations ran successfully: ```bash docker compose logs app | grep -i migration ``` You should see: `All migrations have been run!` ### More troubleshooting For additional troubleshooting options including debug mode, trusted proxy configuration, and artisan commands, see the [Configuration Troubleshooting](./configuration/application#troubleshooting) section. See also [Docker Networking](../user-guide/database-servers#docker-networking) if you're having issues connecting to your database server. --- ## Docker This guide will help you deploy Databasement using Docker. This is the simplest deployment method, using a single container that includes everything you need. ## Prerequisites - [Docker](https://docs.docker.com/engine/install/) installed on your system - A [supported application database](intro#supported-application-database-versions) (SQLite, MySQL, MariaDB, or PostgreSQL) ## Quick Start (SQLite) ### 1. Create Project Directory ```bash mkdir databasement && cd databasement ``` ### 2. Generate Application Key ```bash APP_KEY=$(docker run --rm davidcrty/databasement:1 php artisan key:generate --show) ``` ### 3. Create Environment File Create a `.env` file with your configuration: ```bash title=".env" APP_URL=http://localhost:2226 APP_KEY=base64:your-generated-key-here # Database (SQLite) DB_CONNECTION=sqlite DB_DATABASE=/data/database.sqlite # Enable the background queue worker inside the container ENABLE_QUEUE_WORKER=true ``` ### 4. Run the Container ```bash docker run -d \ --name databasement \ -p 2226:2226 \ --env-file .env \ -v ./databasement-data:/data \ davidcrty/databasement:1 ``` :::note The `ENABLE_QUEUE_WORKER=true` environment variable enables the background queue worker inside the container. This is required for processing backup and restore jobs. When using Docker Compose, the worker runs as a separate service instead. ::: Access the application at http://localhost:2226 :::tip Pin a version See [Versioning](versioning) for available tags. ::: ## Custom User ID (PUID/PGID) By default, the application runs as PUID/PGID `1000`. You can customize this by adding `PUID` and `PGID` to your `.env` file: ```bash title=".env" PUID=1001 PGID=1001 ``` :::tip Find your user's PUID/PGID with `id username`. The container will automatically set the correct permissions on `/data` for the specified PUID/PGID. ::: ### Rootless Containers For rootless Docker or Podman environments, use the `--user` flag. When using this method, the container runs entirely as the specified user and skips the automatic permission fix: ```bash # Create the data directory and set permissions first mkdir /path/to/databasement/data sudo chown 499:499 /path/to/databasement/data docker run -d \ ... --user 499:499 \ ... ``` :::note When using `--user`, you must manually set `/data` directory volume permissions before starting the container since the automatic permission fix requires root: `sudo chown 499:499 /path/to/databasement/data` ::: ## Updating Pull the new image and recreate the container. Migrations run automatically on startup. ```bash docker pull davidcrty/databasement:1 docker stop databasement && docker rm databasement docker run -d \ --name databasement \ -p 2226:2226 \ --env-file .env \ -v ./databasement-data:/data \ davidcrty/databasement:1 ``` See [Versioning](versioning) for available versions. ## Troubleshooting If you encounter issues, see the [Docker Compose Troubleshooting](./docker-compose#troubleshooting) section for common problems and solutions. For additional troubleshooting options including debug mode and configuration issues, see the [Configuration Troubleshooting](./configuration/application#troubleshooting) section. See also [Docker Networking](../user-guide/database-servers#docker-networking) if you're having issues connecting to your database server. --- ## Kubernetes + Helm This guide will help you deploy Databasement on Kubernetes using Helm. [![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/databasement)](https://artifacthub.io/packages/search?repo=databasement) ## Installation ### 1. Add the Helm Repository ```bash helm repo add databasement https://david-crty.github.io/databasement helm repo update ``` ### 2. Generate an Application Key Before deploying, generate an application encryption key: ```bash docker run --rm davidcrty/databasement:1 php artisan key:generate --show ``` Copy the output (e.g., `base64:abc123...`) for use in your values file. ### 3. Create a Values File #### Minimal Configuration (SQLite) For simple deployments using SQLite: ```yaml title="values.yaml" app: url: https://backup.yourdomain.com appKey: value: "base64:your-generated-key-here" ingress: enabled: true className: nginx host: backup.yourdomain.com # For HTTPS using cert-manager: # tlsSecretName: databasement-tls # annotations: # cert-manager.io/cluster-issuer: letsencrypt-prod ``` #### Production Configuration (External Database) For production, we recommend using MySQL or PostgreSQL instead of SQLite (see [supported versions](intro#supported-application-database-versions)): ```yaml title="values.yaml" # ... other app config database: connection: mysql # or pgsql host: your-mysql-host.example.com port: 3306 name: databasement username: databasement password: value: "your-secure-password" ingress: enabled: true className: nginx host: backup.yourdomain.com ``` #### Using Existing Secrets For sensitive values, you can reference existing Kubernetes secrets instead of storing them in your values file: ```yaml title="values.yaml" app: appKey: fromSecret: secretName: "my-app-secret" secretKey: "APP_KEY" database: connection: mysql host: mysql.example.com name: databasement username: databasement password: fromSecret: secretName: "my-db-secret" secretKey: "password" ``` ### 4. Install the Chart ```bash helm upgrade --install databasement databasement/databasement -f values.yaml ``` See [Versioning](versioning) for available chart versions. The chart version matches the application version. ## Configuration For the full list of configurable parameters, see the [values.yaml](https://github.com/david-crty/databasement/blob/main/helm/databasement/values.yaml) file. For all available environment variables, see the [Configuration](./configuration) page. ### Custom Environment Variables Use `extraEnv` to pass additional environment variables: ```yaml extraEnv: AWS_ACCESS_KEY_ID: "your-access-key" AWS_SECRET_ACCESS_KEY: "your-secret-key" AWS_DEFAULT_REGION: "us-east-1" ``` :::tip S3 Storage To store backups in AWS S3 or S3-compatible storage (MinIO, DigitalOcean Spaces, etc.), see the [S3 Storage Configuration](./configuration/backup#s3-storage) section for all available options. ::: ### Environment Variables from Secrets/ConfigMaps Use `extraEnvFrom` to load environment variables from existing secrets or configmaps: ```yaml extraEnvFrom: - secretRef: name: aws-credentials - configMapRef: name: app-config ``` This is useful for injecting credentials managed by external secret management tools (e.g., External Secrets Operator, Sealed Secrets). ### Persistence By default, persistence is enabled with a 10Gi volume: ```yaml persistence: enabled: true storageClass: "" # Uses default storage class size: 10Gi accessModes: - ReadWriteOnce ``` ### Worker Configuration The queue worker runs as a sidecar container by default. You can customize its behavior: ```yaml worker: enabled: true command: "php artisan queue:work --queue=backups,default --tries=3 --timeout=3600" resources: limits: cpu: 500m memory: 512Mi requests: cpu: 100m memory: 256Mi ``` For high-availability setups with an external database, you can run the worker as a separate deployment: ```yaml worker: separateDeployment: true replicaCount: 2 ``` :::note Separate worker deployment requires either ReadWriteMany storage or AWS S3 storage + an external database (MySQL/PostgreSQL). ::: ## Updating Update the `--version` flag and run the same install command. Migrations run automatically on startup. ```bash helm repo update helm upgrade --install databasement databasement/databasement --version 1.0.1 -f values.yaml ``` See [Versioning](versioning) for available versions. ## Troubleshooting For additional troubleshooting options including debug mode and configuration issues, see the [Configuration Troubleshooting](./configuration/application#troubleshooting) section. --- ## Native Ubuntu Installation This guide will help you install Databasement directly on Ubuntu without Docker. This is useful for environments where Docker is not available or when you prefer a traditional installation. ## Prerequisites - Ubuntu 22.04 LTS or 24.04 LTS - Root or sudo access - A web server (Nginx or Apache) - A [supported application database](intro#supported-application-database-versions) (SQLite, MySQL, MariaDB, or PostgreSQL) ## Install PHP 8.4 ```bash # Add PHP repository sudo add-apt-repository ppa:ondrej/php -y sudo apt update # Install PHP 8.4 and required extensions sudo apt install -y \ php8.4 \ php8.4-fpm \ php8.4-cli \ php8.4-common \ php8.4-curl \ php8.4-mbstring \ php8.4-xml \ php8.4-zip \ php8.4-pdo \ php8.4-mysql \ php8.4-pgsql \ php8.4-sqlite3 \ php8.4-intl \ php8.4-pcntl \ php8.4-opcache ``` ## Install Database CLI Tools Databasement requires database CLI tools to perform backup and restore operations. ### For MySQL/MariaDB backups ```bash # Install MariaDB client (includes mariadb-dump and mariadb commands) sudo apt install -y mariadb-client ``` :::note Databasement uses the MariaDB CLI tools (`mariadb-dump`, `mariadb`) to back up and restore both MariaDB and MySQL servers. ::: ### For PostgreSQL backups ```bash # Install PostgreSQL client (includes pg_dump and psql commands) sudo apt install -y postgresql-client ``` ### For SQLite backups SQLite support is built into PHP - no additional tools needed. ## Install Composer ```bash curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer ``` ## Install Node.js ```bash curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt install -y nodejs ``` ## Install Nginx ```bash sudo apt install -y nginx ``` ## Download and Configure Databasement ```bash # Clone the repository cd /var/www/databasement git clone https://github.com/David-Crty/databasement.git . # Install PHP dependencies composer install --no-dev --optimize-autoloader # Install Node dependencies and build assets npm install npm run build # Publish Livewire assets php artisan vendor:publish --force --tag=livewire:assets ``` ## Configure Environment ```bash # Generate application key php artisan key:generate ``` Edit the `.env` file with your configuration: ```bash nano .env ``` Essential settings: ```bash APP_ENV=production APP_DEBUG=false APP_URL=https://your-domain.com # Database for Databasement (choose one) # SQLite (simplest) DB_CONNECTION=sqlite DB_DATABASE=/var/www/databasement/database/database.sqlite # Or MySQL # DB_CONNECTION=mysql # DB_HOST=127.0.0.1 # DB_PORT=3306 # DB_DATABASE=databasement # DB_USERNAME=databasement # DB_PASSWORD=your-password # Or PostgreSQL # DB_CONNECTION=pgsql # DB_HOST=127.0.0.1 # DB_PORT=5432 # DB_DATABASE=databasement # DB_USERNAME=databasement # DB_PASSWORD=your-password # Backup working directory BACKUP_WORKING_DIRECTORY=/tmp/backups ``` For all available environment variables, see the [Configuration](./configuration) page. :::tip S3 Storage To store backups in AWS S3 or S3-compatible storage (MinIO, DigitalOcean Spaces, etc.), see the [S3 Storage Configuration](./configuration/backup#s3-storage) section. ::: ## Run Database Migrations ```bash # Run migrations cd /var/www/databasement php artisan migrate --force ``` ## Configure Nginx Create a new Nginx configuration: ```bash sudo nano /etc/nginx/sites-available/databasement ``` Add the following configuration: ```nginx server { listen 80; server_name your-domain.com; root /var/www/databasement/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.4-fpm.sock; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; fastcgi_hide_header X-Powered-By; } location ~ /\.(?!well-known).* { deny all; } } ``` Enable the site: ```bash sudo ln -s /etc/nginx/sites-available/databasement /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ## Configure Queue Worker (Systemd) The queue worker is **required** to process backup and restore jobs. Create a systemd service: ```bash sudo nano /etc/systemd/system/databasement-queue.service ``` Add the following: ```ini [Unit] Description=Databasement Queue Worker After=network.target [Service] User=www-data Group=www-data Restart=always RestartSec=5 WorkingDirectory=/var/www/databasement ExecStart=/usr/bin/php artisan queue:work --queue=backups,default --tries=3 --timeout=3600 --sleep=3 --max-jobs=1000 [Install] WantedBy=multi-user.target ``` Enable and start the service: ```bash sudo systemctl daemon-reload sudo systemctl enable databasement-queue sudo systemctl start databasement-queue ``` Check status: ```bash sudo systemctl status databasement-queue ``` ## Configure Scheduler (Cron) The scheduler runs automated backups based on your configured schedules. ```bash crontab -e ``` Add this line: ```cron * * * * * cd /var/www/databasement && php artisan schedule:run >> /dev/null 2>&1 ``` ## Verification 1. Open your browser and navigate to your domain 2. Create your first user account 3. Add a database server and test the connection 4. Create a backup to verify everything works ## Troubleshooting - Check Queue Worker Logs ```bash sudo journalctl -u databasement-queue -f ``` - Check PHP-FPM Logs - Check Nginx Logs ### Test Database CLI Tools ```bash # Test MariaDB/MySQL client mariadb --version # or: mysql --version # Test PostgreSQL client psql --version # Test pg_dump pg_dump --version ``` ### Run Artisan Commands ```bash php artisan migrate:status php artisan config:show database ``` ### More troubleshooting For additional troubleshooting options including debug mode and trusted proxy configuration, see the [Configuration Troubleshooting](./configuration/application#troubleshooting) section. ## Updating Databasement Check the [GitHub Releases](https://github.com/David-Crty/databasement/releases) page for available versions, then update to a specific release: ```bash cd /var/www/databasement # Fetch tags and checkout the desired version git fetch --tags git checkout v1.0.1 # Install dependencies composer install --no-dev --optimize-autoloader npm install npm run build # Run migrations php artisan migrate --force # Clear caches php artisan optimize:clear # Restart queue worker sudo systemctl restart databasement-queue ``` --- ## Application Core application settings including database, timezone, proxy configuration, and logging. ## Application Settings | Variable | Description | Default | | ----------------------- | ------------------------------------------------ | ----------------------- | | `APP_KEY` | Application encryption key (required) | - | | `APP_URL` | Full URL where the app is accessible | `http://localhost:2226` | | `APP_DEBUG` | Enable debug mode (set to `false` in production) | `false` | | `APP_DISPLAY_TIMEZONE` | Timezone for UI, backup filenames, and crons | `UTC` | ### Generating the Application Key The `APP_KEY` is required for encryption. Generate one with: ```bash docker run --rm davidcrty/databasement:1 php artisan key:generate --show ``` Copy the output (e.g., `base64:xxxx...`) and set it as `APP_KEY`. ### Timezone Configuration Set `APP_DISPLAY_TIMEZONE` to display dates, backup filenames, and schedule times in your local timezone. A cron like `0 2 * * *` will then fire at 02:00 in that zone. ```bash APP_DISPLAY_TIMEZONE=Europe/London ``` See the [list of supported timezones](https://www.php.net/manual/en/timezones.php). Data is always stored in UTC internally. If you previously set `TZ`, it is migrated automatically — no action needed. ## Database Configuration Databasement needs a database to store its own data (users, servers, backup configurations). ### SQLite (Simplest) ```bash DB_CONNECTION=sqlite DB_DATABASE=/data/database.sqlite ``` :::note When using SQLite, make sure to mount a volume for `/data` to persist data. ::: ### MySQL / MariaDB Create a database and user for Databasement on your MySQL server: **MySQL:** ```sql CREATE DATABASE databasement CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'databasement'@'%' IDENTIFIED BY 'your-secure-password'; GRANT ALL PRIVILEGES ON databasement.* TO 'databasement'@'%'; FLUSH PRIVILEGES; ``` ```bash DB_CONNECTION=mysql DB_HOST=your-mysql-host DB_PORT=3306 DB_DATABASE=databasement DB_USERNAME=databasement DB_PASSWORD=your-secure-password ``` ### PostgreSQL Create a database and user for Databasement on your PostgreSQL server: **PostgreSQL:** ```sql CREATE DATABASE databasement; CREATE USER databasement WITH ENCRYPTED PASSWORD 'your-secure-password'; GRANT ALL PRIVILEGES ON DATABASE databasement TO databasement; ``` ```bash DB_CONNECTION=pgsql DB_HOST=your-postgres-host DB_PORT=5432 DB_DATABASE=databasement DB_USERNAME=databasement DB_PASSWORD=your-secure-password ``` ## Reverse Proxy / Trusted Proxies When running behind a reverse proxy (nginx, Traefik, Kubernetes Ingress), configure trusted proxies so Laravel can correctly determine the client IP and protocol. See the [Troubleshooting section](#troubleshooting) if you have issues with proxy configuration. | Variable | Description | Default | | ----------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------- | | `TRUSTED_PROXIES` | IP addresses or CIDR ranges of trusted proxies | `127.0.0.0/8,10.0.0.0/8,100.64.0.0/10,169.254.0.0/16,172.16.0.0/12,192.168.0.0/16` | **Alternative values:** - `*` - Trust all proxies (simplest option for containerized environments) - Comma-separated IPs/CIDRs: `10.0.0.1,192.168.1.0/24` - Trust specific proxies only - Empty - Trust no proxies :::info Checks the [Troubleshooting section](#troubleshooting) for help with proxy configuration. ::: ## Logging | Variable | Description | Default | | ------------- | ----------------- | -------- | | `LOG_CHANNEL` | Logging channel | `stderr` | | `LOG_LEVEL` | Minimum log level | `debug` | For production, `stderr` is recommended as logs will be captured by Docker. ## Complete Example Here's a complete `.env` file for a production deployment with MySQL: ```bash # Application APP_DEBUG=false APP_URL=https://backup.yourdomain.com APP_KEY=base64:your-generated-key-here # APP_DISPLAY_TIMEZONE=UTC # timezone for UI, backup filenames, and schedules # Database (for Databasement itself) DB_CONNECTION=mysql DB_HOST=mysql.yourdomain.com DB_PORT=3306 DB_DATABASE=databasement DB_USERNAME=databasement DB_PASSWORD=secure-password-here # Logging LOG_CHANNEL=stderr LOG_LEVEL=warning ``` ## Troubleshooting ### Enable Debug Mode Enable debug mode to access detailed diagnostics: ```bash APP_DEBUG=true ``` Then visit `https://your-domain.com/health/debug` to view: - Current IP address and whether it's from a trusted proxy - Request headers (including `X-Forwarded-For`, `X-Forwarded-Proto`) - Application configuration ### Debugging Trusted Proxies If your application shows HTTP instead of HTTPS, or shows the wrong client IP: 1. **Enable debug mode** (see above) 2. **Visit `/health/debug`** and check: - `is_trusted_proxy`: Should be `true` - `secure`: Should be `true` for HTTPS - `headers`: Check `x-forwarded-for` and `x-forwarded-proto` 3. **Common issues:** - `is_trusted_proxy: false` → The proxy IP is not in your `TRUSTED_PROXIES` list - `secure: false` with HTTPS → Trusted proxy not configured, so `x-forwarded-proto` header is ignored 4. **Quick fix:** Set `TRUSTED_PROXIES=*` to trust all proxies ### More troubleshooting If you encounter issues, see the [Docker Compose Troubleshooting](../docker-compose#troubleshooting) section for common problems and solutions. See also [Docker Networking](../../user-guide/database-servers#docker-networking) if you're having issues connecting to your database server. ### Run Artisan Commands ```bash php artisan migrate:status # Check database migrations php artisan config:show database # View database configuration ``` ### Get Help - Check the logs: `docker compose logs app` - Report issues on [GitHub](https://github.com/david-crty/databasement/issues) --- ## Backup Backup settings (compression, schedules, timeouts, etc.) can be configured directly from the **Configuration** page in the web UI. This page covers additional setup that requires environment variables. ## Encrypted Backups When using `encrypted` compression, backups are encrypted with AES-256 using 7-Zip. The encryption key defaults to `APP_KEY`, but you can set a dedicated key: ```bash BACKUP_ENCRYPTION_KEY=base64:your-32-byte-key-here ``` You can generate a key with: ```bash echo "base64:$(openssl rand -base64 32)" ``` :::warning If you change the encryption key, you will not be able to restore backups that were encrypted with the previous key. Keep your encryption key safe and backed up separately. ::: ## S3 Storage Databasement supports AWS S3 and S3-compatible storage (MinIO, DigitalOcean Spaces, etc.) for backup volumes. All S3 settings (region, credentials, endpoints) are configured **per-volume** in the web UI when creating or editing an S3 volume. See the [Volumes user guide](../../user-guide/volumes#s3-storage) for field descriptions. ### S3 IAM Permissions The AWS credentials used by each S3 volume need these permissions: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::your-bucket-name", "arn:aws:s3:::your-bucket-name/*" ] } ] } ``` ### Credentials You can provide explicit **Access Key ID** and **Secret Access Key** in the volume form. However, credentials are optional — when left blank, the AWS SDK uses its [default credential chain](https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_credentials.html): - Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) - EC2/ECS instance roles - EKS IRSA (IAM Roles for Service Accounts) This means deployments on AWS infrastructure can work without storing any credentials in the database. ### S3-Compatible Storage (MinIO, etc.) For S3-compatible providers, set the **Custom Endpoint** and enable **Use Path-Style Endpoint** in the Advanced S3 Settings section of the volume form. :::tip If your internal endpoint differs from the public URL (e.g., `http://minio:9000` vs `http://localhost:9000`), set the **Public Endpoint** field so presigned download URLs work correctly in your browser. ::: ### Migration from Environment Variables If you previously configured S3 via environment variables (`AWS_ACCESS_KEY_ID`, `AWS_REGION`, etc.), the migration automatically copies those values into each existing S3 volume's config. After upgrading, you can remove the AWS environment variables from your `.env` file. --- ## Notification Notification settings (enable/disable, channels, recipients) can be configured directly from the **Configuration** page in the web UI. This page covers additional setup guides for each channel. ## Email {#email} Databasement uses Laravel's mail system (Symfony Mailer). Configure your mail driver with these environment variables: --- ### Basic SMTP configuration (STARTTLS – typical 587) ```bash MAIL_MAILER=smtp MAIL_HOST=smtp.example.com MAIL_PORT=587 MAIL_SCHEME=smtp MAIL_USERNAME=your-username MAIL_PASSWORD=your-password MAIL_FROM_ADDRESS=databasement@example.com MAIL_FROM_NAME="Databasement" ``` `smtp` uses plain SMTP and automatically negotiates STARTTLS if the server supports it (commonly port 587). --- ### SMTPS (implicit TLS – typical 465) ```bash MAIL_MAILER=smtp MAIL_HOST=smtp.example.com MAIL_PORT=465 MAIL_SCHEME=smtps MAIL_USERNAME=your-username MAIL_PASSWORD=your-password MAIL_FROM_ADDRESS=databasement@example.com MAIL_FROM_NAME="Databasement" ``` `smtps` enables implicit TLS from the beginning of the connection. --- ### Unencrypted SMTP / no forced encryption (port 25) ```bash MAIL_MAILER=smtp MAIL_HOST=smtp.example.com MAIL_PORT=25 MAIL_SCHEME=smtp ``` If the server advertises `STARTTLS`, Symfony will attempt to use it automatically. If you **must explicitly disable STARTTLS**, use a DSN: ```bash MAIL_MAILER=smtp MAIL_URL=smtp://smtp.example.com:25?auto_tls=false ``` --- ### Using a DSN (advanced configuration) You may configure the full connection using `MAIL_URL`: ```bash MAIL_MAILER=smtp MAIL_URL=smtp://user:pass@smtp.example.com:587 ``` Or with implicit TLS: ```bash MAIL_MAILER=smtp MAIL_URL=smtps://user:pass@smtp.example.com:465 ``` > **Note:** If your username or password contains special URI characters (e.g. `@`, `:`, `+`, `#`), percent-encode them in the DSN (e.g. `@` → `%40`). When `MAIL_URL` is defined, it overrides `MAIL_HOST`, `MAIL_PORT`, `MAIL_USERNAME`, `MAIL_PASSWORD`, and `MAIL_SCHEME`. --- ### Removed option `MAIL_ENCRYPTION` is no longer used. Encryption behavior is controlled exclusively by the DSN scheme (`smtp` or `smtps`) and optional DSN parameters. ## Slack {#slack} To receive failure notifications in Slack, you need to create an Incoming Webhook: 1. Go to [Slack API Apps](https://api.slack.com/apps) 2. Click **Create New App** > **From scratch** 3. Name your app (e.g., "Databasement") and select your workspace 4. Go to **Incoming Webhooks** and toggle it on 5. Click **Add New Webhook to Workspace** 6. Select the channel where you want notifications 7. Copy the webhook URL and paste it in the Configuration page ## Discord Webhook {#discord-webhook} The simplest way to receive failure notifications in Discord — no bot required. 1. Open **Server Settings** > **Integrations** > **Webhooks** 2. Click **New Webhook** 3. Choose the channel where you want notifications 4. Click **Copy Webhook URL** Paste the **Webhook URL** on the Configuration page (select the **Discord (Webhook)** channel). > **Tip:** If you just need notifications in a single channel, Discord Webhook is easier to set up than the bot-based integration below. ## Discord Bot {#discord} To receive failure notifications in Discord via a bot, you need a bot token and a channel ID. This gives more flexibility (e.g., sending to multiple channels) but requires more setup. ### Creating a Discord Bot 1. Go to [Discord Developer Portal](https://discord.com/developers/applications) 2. Click **New Application** and give it a name (e.g., "Databasement") 3. Go to **Bot** in the sidebar and click **Add Bot** 4. Under **Token**, click **Copy** to get your bot token 5. Go to **OAuth2** > **URL Generator** 6. Select scopes: `bot` 7. Select bot permissions: `Send Messages`, `Embed Links` 8. Copy the generated URL and open it to invite the bot to your server ### Getting a Channel ID 1. Open Discord and go to **User Settings** > **Advanced** 2. Enable **Developer Mode** 3. Right-click the channel where you want notifications 4. Click **Copy Channel ID** Enter both the **Bot Token** and **Channel ID** on the Configuration page (select the **Discord (Bot)** channel). ## Telegram {#telegram} To receive failure notifications via Telegram, you need a bot token and a chat ID. ### Creating a Telegram Bot 1. Open Telegram and search for **@BotFather** 2. Send `/newbot` and follow the prompts to name your bot 3. BotFather will give you a **Bot Token** — copy it ### Getting a Chat ID 1. Add your bot to the group or start a direct chat with it 2. Send a message to the bot 3. Open `https://api.telegram.org/bot/getUpdates` in a browser 4. Find the `chat.id` value in the JSON response Enter both the **Bot Token** and **Chat ID** on the Configuration page. ## Pushover {#pushover} [Pushover](https://pushover.net/) delivers push notifications to your phone and desktop. 1. Create an account at [pushover.net](https://pushover.net/) 2. Copy your **User Key** from the dashboard 3. Go to **Create an Application/API Token** 4. Name it (e.g., "Databasement") and copy the **App Token** Enter both the **App Token** and **User Key** on the Configuration page. ## Gotify {#gotify} [Gotify](https://gotify.net/) is a self-hosted push notification server. 1. Log in to your Gotify server 2. Go to **Apps** and create a new application (e.g., "Databasement") 3. Copy the **App Token** Enter your **Gotify Server URL** (e.g., `https://gotify.example.com`) and the **App Token** on the Configuration page. ## Webhook {#webhook} Send failure notifications as JSON payloads to any HTTP endpoint. Enter your **Webhook URL** on the Configuration page. Optionally, provide a **Webhook Secret** to authenticate requests via the `X-Webhook-Token` header. ### Request Format Notifications are sent as `POST` requests with a JSON body: ```json { "event": "BackupFailedNotification", "title": "Backup Failed: Production DB", "body": "A backup job has failed.", "fields": { "Server": "Production DB", "Database": "myapp" }, "error": "Connection refused", "action_url": "https://your-instance.com/backup-jobs/...", "timestamp": "2025-01-15T02:00:00+00:00" } ``` ### Headers | Header | Description | |--------|-------------| | `Content-Type` | `application/json` | | `X-Webhook-Token` | The configured secret (only if a secret is configured) | ### Tip: Using with Apprise The webhook channel can be pointed at an [Apprise](https://github.com/caronc/apprise) API endpoint to relay notifications to 100+ services (Ntfy, Matrix, Mattermost, etc.). Set the Apprise stateless endpoint (e.g., `https://apprise.example.com/notify/`) as your Webhook URL. ## What Gets Notified Notifications are sent only for **failures**: - **Backup failures**: When a scheduled or manual backup fails - **Restore failures**: When a restore operation fails - **Missing snapshots**: When snapshot file verification detects missing backup files on storage volumes Successful operations do not trigger notifications. ## Notification Content Each notification includes: - Server name - Database name - Error message - Timestamp - Direct link to the failed job details --- ## SSO Databasement supports OAuth authentication, allowing users to log in using external identity providers. This can be used alongside or instead of traditional password authentication. ## Supported Providers - **Google** - Google Workspace and personal accounts - **GitHub** - GitHub accounts - **GitLab** - GitLab.com or self-hosted GitLab - **Generic OIDC** - Any OpenID Connect provider (Keycloak, Authentik, Dex, Okta, etc.) :::tip Need another provider? Laravel Socialite supports [many more providers](https://socialiteproviders.com/) including Facebook, Microsoft, Apple, Slack, and 100+ others. Feel free to submit a PR to add support for additional providers. ::: ## Configuration OAuth is configured via environment variables. Each provider can be enabled independently. ### Google 1. Create OAuth credentials in [Google Cloud Console](https://console.cloud.google.com/apis/credentials) 2. Set authorized redirect URI to: `https://your-domain.com/oauth/google/callback` 3. Configure environment variables: ```env OAUTH_GOOGLE_ENABLED=true OAUTH_GOOGLE_CLIENT_ID=your-client-id OAUTH_GOOGLE_CLIENT_SECRET=your-client-secret ``` ### GitHub 1. Create an OAuth App in [GitHub Developer Settings](https://github.com/settings/developers) 2. Set authorization callback URL to: `https://your-domain.com/oauth/github/callback` 3. Configure environment variables: ```env OAUTH_GITHUB_ENABLED=true OAUTH_GITHUB_CLIENT_ID=your-client-id OAUTH_GITHUB_CLIENT_SECRET=your-client-secret ``` ### GitLab 1. Create an OAuth application in GitLab (Admin Area > Applications or User Settings > Applications) 2. Set redirect URI to: `https://your-domain.com/oauth/gitlab/callback` 3. Configure environment variables: ```env OAUTH_GITLAB_ENABLED=true OAUTH_GITLAB_CLIENT_ID=your-application-id OAUTH_GITLAB_CLIENT_SECRET=your-secret OAUTH_GITLAB_HOST=https://gitlab.com # Or your self-hosted GitLab URL ``` ### Generic OIDC (Keycloak, Authentik, etc.) For any OpenID Connect compatible provider: 1. Create a client/application in your identity provider 2. Set redirect URI to: `https://your-domain.com/oauth/oidc/callback` 3. Configure environment variables: ```env OAUTH_OIDC_ENABLED=true OAUTH_OIDC_CLIENT_ID=your-client-id OAUTH_OIDC_CLIENT_SECRET=your-client-secret OAUTH_OIDC_BASE_URL=https://your-idp.com/realms/your-realm # The OIDC base URL OAUTH_OIDC_LABEL=SSO # Button label on login page ``` #### Keycloak Setup 1. In Keycloak Admin Console, go to **Clients** and click **Create client** 2. Configure the client: - **Client ID**: `databasement` (or your preferred name) - **Client authentication**: **On** (required for confidential clients) - **Authentication flow**: Check **Standard flow** (Authorization Code Flow) 3. In the **Settings** tab, configure the URLs (replace `databasement.example.com` with your domain): | Field | Value | | ------------------------------- | ------------------------------------------------------ | | Root URL | `https://databasement.example.com` | | Home URL | `https://databasement.example.com` | | Valid redirect URIs | `https://databasement.example.com/oauth/oidc/callback` | | Valid post logout redirect URIs | `https://databasement.example.com` | | Web origins | `https://databasement.example.com` | 4. Go to the **Credentials** tab and copy the **Client secret** 5. Configure environment variables: ```env OAUTH_OIDC_ENABLED=true OAUTH_OIDC_CLIENT_ID=databasement OAUTH_OIDC_CLIENT_SECRET=your-client-secret OAUTH_OIDC_BASE_URL=https://keycloak.example.com/realms/your-realm OAUTH_OIDC_LABEL=Keycloak ``` :::tip Finding the Issuer URL The issuer URL follows the pattern `https://your-keycloak-server/realms/your-realm-name`. You can find it in **Realm Settings** > **General** > **Endpoints** > **OpenID Endpoint Configuration**. ::: #### Authentik Example ```env OAUTH_OIDC_ENABLED=true OAUTH_OIDC_CLIENT_ID=databasement OAUTH_OIDC_CLIENT_SECRET=your-secret OAUTH_OIDC_BASE_URL=https://authentik.example.com/application/o/databasement/ OAUTH_OIDC_LABEL=Authentik ``` ## OAuth-Only Mode To enforce OAuth/SSO as the only sign-in method, hide the password login form, and reject password authentication on the server: ```env OAUTH_ONLY_MODE=true # Default: false ``` When enabled: - The password fields are hidden on the login page; users only see OAuth provider buttons. - Password authentication is rejected even if a request bypasses the UI. - The "forgot password" and "reset password" routes return `404`. - First-user bootstrap registration (when no users exist yet) still works, so the instance can be initialized before connecting an identity provider. :::warning Make sure at least one OAuth provider is configured and working before enabling `OAUTH_ONLY_MODE` — otherwise no one will be able to sign in. ::: ## User Creation Settings ### Auto-Create Users When enabled (default), new users are automatically created when they log in via OAuth for the first time: ```env OAUTH_AUTO_CREATE_USERS=true # Default: true ``` Set to `false` to only allow existing users to log in via OAuth. ### Default Role New users created via OAuth are assigned this role: ```env OAUTH_DEFAULT_ROLE=member # Options: viewer, member, admin ``` ### Default Organization By default, auto-created OAuth users join the default organization. To assign them to a different organization instead, set: ```env OAUTH_DEFAULT_ORGANIZATION_ID=01JA2B3C4D5E6F7G8H9J0KMNPQ # Organization ULID ``` You can find the organization ID in **Configuration > Organizations**. See the [Organizations guide](/user-guide/organizations) for more details on multi-org setups. ### Auto-Link by Email When enabled (default), OAuth logins are automatically linked to existing users with matching email addresses: ```env OAUTH_AUTO_LINK_BY_EMAIL=true # Default: true ``` ## OIDC Group-Based Role Mapping When using a Generic OIDC provider (Keycloak, Authentik, Dex, etc.), you can map IdP groups to Databasement roles automatically. This lets you control who can access Databasement and what role they get — all from your identity provider. ### How It Works 1. Your IdP includes a `groups` claim in the OIDC token (e.g., `["devops", "databasement-admins"]`) 2. Databasement checks the user's groups against your configured mappings 3. The user gets the highest-priority matching role: **admin > member > viewer** 4. Roles are synced on every login, so changes in the IdP take effect immediately ### Configuration First, make sure your IdP sends groups in the token. Most IdPs require requesting the `groups` scope: ```env OAUTH_OIDC_SCOPES=groups ``` Then map IdP groups to Databasement roles. Use comma-separated values if multiple IdP groups should map to the same role: ```env OAUTH_OIDC_ROLE_MAP_ADMIN=databasement-admins OAUTH_OIDC_ROLE_MAP_MEMBER=databasement-members,devops-team OAUTH_OIDC_ROLE_MAP_VIEWER=databasement-viewers,interns ``` When at least one `ROLE_MAP` is set, mapping is active. Users whose groups don't match any mapping get the `OAUTH_DEFAULT_ROLE`. ### Strict Mode To deny access to users without a matching group (instead of falling back to the default role): ```env OAUTH_OIDC_ROLE_STRICT=true ``` With strict mode, users who don't have any matching group are rejected at login — even returning users whose groups have been revoked in the IdP. ### Custom Claim Name By default, Databasement reads the `groups` claim. If your IdP uses a different claim name (e.g., `roles` or `realm_access`): ```env OAUTH_OIDC_ROLE_CLAIM=roles ``` ### Full Example (Keycloak) ```env OAUTH_OIDC_ENABLED=true OAUTH_OIDC_CLIENT_ID=databasement OAUTH_OIDC_CLIENT_SECRET=your-secret OAUTH_OIDC_BASE_URL=https://keycloak.example.com/realms/your-realm OAUTH_OIDC_LABEL=Keycloak OAUTH_OIDC_SCOPES=groups OAUTH_OIDC_ROLE_MAP_ADMIN=databasement-admins OAUTH_OIDC_ROLE_MAP_MEMBER=databasement-members OAUTH_OIDC_ROLE_MAP_VIEWER=databasement-viewers OAUTH_OIDC_ROLE_STRICT=true ``` :::tip Keycloak Group Mapper In Keycloak, go to your client > **Client scopes** > **databasement-dedicated** > **Mappers** > **Add mapper** > **Group Membership**. Set the token claim name to `groups` and disable "Full group path" to get flat group names. ::: ### Environment Variables Reference | Variable | Default | Description | | --- | --- | --- | | `OAUTH_OIDC_SCOPES` | *(empty)* | Extra OIDC scopes to request (comma-separated, e.g., `groups`) | | `OAUTH_OIDC_ROLE_CLAIM` | `groups` | JWT claim containing the user's groups | | `OAUTH_OIDC_ROLE_MAP_ADMIN` | *(empty)* | IdP groups that map to the **admin** role (comma-separated) | | `OAUTH_OIDC_ROLE_MAP_MEMBER` | *(empty)* | IdP groups that map to the **member** role (comma-separated) | | `OAUTH_OIDC_ROLE_MAP_VIEWER` | *(empty)* | IdP groups that map to the **viewer** role (comma-separated) | | `OAUTH_OIDC_ROLE_STRICT` | `false` | Deny login when no group matches (instead of using default role) | For local development OAuth testing, see the [Development Guide](/contributing/development#oauth--sso-testing). --- ## Versioning Databasement follows [semantic versioning](https://semver.org/). The Docker images, Helm chart, and application all share the same version number — version `1.0.1` means the same release everywhere. Check the [GitHub Releases](https://github.com/David-Crty/databasement/releases) page for the full changelog and available versions. ## Docker Image Tags Docker images are available on [Docker Hub](https://hub.docker.com/r/davidcrty/databasement). When a new version is released (e.g., `1.0.1`), the following tags are published: - `davidcrty/databasement:1.0.1` — exact version (pinned) - `davidcrty/databasement:1.0` — latest patch in the 1.0.x line - `davidcrty/databasement:1` — latest release in the 1.x.x line - `davidcrty/databasement:latest` — most recent release Use an exact version tag for production deployments. Use `latest` or a major/minor tag if you want automatic updates with tools like [Renovate](https://docs.renovatebot.com/) or [Watchtower](https://containrrr.dev/watchtower/). ## Helm Chart The Helm chart uses the same version as the application — installing chart version `1.0.1` deploys app version `1.0.1`. ### Install ```bash helm repo add databasement https://david-crty.github.io/databasement helm repo update helm install databasement databasement/databasement --version 1.0.1 ``` ### Update ```bash helm repo update helm upgrade databasement databasement/databasement --version 1.0.1 ``` ### Use as a dependency In your `Chart.yaml`: ```yaml dependencies: - name: databasement version: "1.0.1" repository: "https://david-crty.github.io/databasement" ``` Then run `helm dependency update`. For full Helm configuration options, see the [Kubernetes + Helm](./kubernetes-helm) guide. --- ## Introduction(User-guide) Welcome to the **User Guide** for **Databasement**! Databasement is a web-based tool for managing database backups. This guide covers how to use the application to: - Register and manage database servers - Configure backup schedules - Create and restore snapshots - Manage storage volumes ## Getting Started After installing Databasement, access the web interface and create your first account. ### 1. Create Your Account When you first access Databasement, you'll be prompted to register. Create an account with: - Your name - Email address - Password Two-factor authentication can be enabled later in your account settings for additional security. ### 2. Add a Database Server Navigate to **Database Servers** and click **Add Server**. You'll need: - **Name**: A friendly name for this server (e.g., "Production MySQL") - **Type**: MySQL, PostgreSQL, MariaDB, Microsoft SQL Server, MongoDB, SQLite, Firebird, or Redis/Valkey - **Host**: The server hostname or IP address - **Port**: The database port (default: 3306 for MySQL, 5432 for PostgreSQL, 1433 for SQL Server, 27017 for MongoDB, 3050 for Firebird, 6379 for Redis) - **Username**: Database user with backup privileges - **Password**: The database password Click **Test Connection** to verify the connection before saving. ### 3. Create a Storage Volume Before creating backups, you need a storage volume. Navigate to **Volumes** and click **Add Volume**. Databasement supports: - **Local storage**: Store backups on the server's filesystem - **S3-compatible storage**: Amazon S3, MinIO, DigitalOcean Spaces, etc. ### 4. Create Your First Backup Go to **Database Servers**, find your server, and click **Backup**. Select the database and storage volume, then start the backup. Backups run asynchronously - you can navigate away and check progress later on the **Snapshots** page. ### 5. Restore a Snapshot To restore a backup, go to **Snapshots**, find the snapshot you want to restore, and click **Restore**. You can restore to: - The same server and database (rollback) - A different database on the same server - A completely different server (cross-server restore) ## Next Steps - [Managing Database Servers](/user-guide/database-servers) - [Configuring Backups](/user-guide/backups) - [Working with Snapshots](/user-guide/snapshots) - [Storage Volumes](/user-guide/volumes) - [Permissions](/user-guide/permissions) --- ## Database Servers Database servers are the source of your backups. Databasement can connect to and backup MySQL, PostgreSQL, MariaDB, Microsoft SQL Server, MongoDB, SQLite, Firebird, and Redis/Valkey servers. ## Supported Versions Databasement uses standard CLI tools to perform backup and restore operations. The table below shows which database engine versions are supported, based on the CLI tools shipped in the Docker image. | Engine | Supported Versions | CLI Tool | Restore | |------------|------------------------------|------------------------------|---------| | MySQL | 5.6, 5.7, 8.x, 9.x | `mariadb-dump` | Yes | | MariaDB | 10.x, 11.x, 12.x | `mariadb-dump` | Yes | | PostgreSQL | 12, 13, 14, 15, 16, 17, 18 | `pg_dump` v18 | Yes | | SQL Server | 2017, 2019, 2022, Azure SQL | `sqlpackage` (`.dacpac`) | Yes | | MongoDB | 4.2, 4.4, 5.0, 6.0, 7.0, 8.0 | `mongodump` / `mongorestore` | Yes | | SQLite | 3.x | File copy | Yes | | Firebird | 3.x, 4.x, 5.x | `gbak` v5 | Yes | | Redis | 2.8+ | `redis-cli --rdb` | No | | Valkey | 7.2+ | `redis-cli --rdb` | No | :::info How this works - **MySQL / MariaDB**: Databasement ships the MariaDB 11.4 client (`mariadb-dump`), which is wire-protocol compatible with MySQL servers. - **PostgreSQL**: The `pg_dump` v18 client can dump from any server version back to 9.2. Versions below 12 have reached end-of-life and are not recommended. - **SQL Server**: Backups are extracted as `.dacpac` files (schema + table data) using Microsoft's `sqlpackage` CLI (`/Action:Extract`) and re-applied with `/Action:Publish`. Server-bound objects (logins, users, permissions, role memberships) are excluded so backups stay portable across instances and don't fail on Windows-auth principals like `[NT AUTHORITY\SYSTEM]`. Works against on-prem SQL Server 2017+ and Azure SQL Database. Connections use the `pdo_sqlsrv` PHP extension. - **MongoDB**: The MongoDB Database Tools (`mongodump` / `mongorestore`) officially support server versions 4.2 through 8.0. - **SQLite**: Backups are performed by copying the database file over SFTP. The SQLite 3.x file format has been backwards-compatible since 3.0.0 (2004). - **Firebird**: Backups use `gbak` to produce a portable `.fbk` transportable backup file; restore replays it with `gbak -rep`, which replaces the target `.fdb` if one already exists. Databasement ships the Firebird 5 client, which can back up and restore Firebird 3.x, 4.x, and 5.x servers. Each `.fdb` file on the server is its own database, so backup configuration is path-based (like SQLite) rather than name-based. - **Redis / Valkey**: `redis-cli --rdb` creates a point-in-time RDB snapshot via the replication protocol. Valkey 7.2+ is supported as a drop-in replacement for Redis. Restore is not supported. ::: ## Connection Requirements ### MySQL / MariaDB #### Creating the user ```sql CREATE USER 'databasement'@'%' IDENTIFIED BY 'your_secure_password'; ``` #### Permissions for backup and restore (all databases) ```sql GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, PROCESS, EVENT, RELOAD, CREATE, DROP, ALTER, INDEX, INSERT, UPDATE, DELETE, REFERENCES ON *.* TO 'databasement'@'%'; FLUSH PRIVILEGES; ``` :::note[Single database only] To restrict the user to a single database, replace `*.*` with `database_name.*`. Note that with single-database permissions, the user cannot create or drop the database itself - you'll need to ensure the target database exists before restoring. ::: ### PostgreSQL #### Creating the user ```sql CREATE USER databasement WITH PASSWORD 'your_secure_password'; ``` #### Permissions for backup and restore (all databases) For full backup and restore capabilities, the user needs elevated privileges. The method depends on your PostgreSQL setup: #### Self-hosted PostgreSQL ```sql -- Option 1: Superuser (full access) ALTER USER databasement WITH SUPERUSER; -- Option 2: Create database privilege (can create/drop databases for restore) ALTER USER databasement WITH CREATEDB; ``` #### AWS RDS PostgreSQL RDS doesn't allow `SUPERUSER`. Grant the `rds_superuser` role instead: ```sql GRANT rds_superuser TO databasement; ``` #### Azure Database for PostgreSQL Azure uses the `azure_pg_admin` role: ```sql GRANT azure_pg_admin TO databasement; ``` #### Additional grants for non-superuser setups If not using superuser/rds_superuser/azure_pg_admin, grant access to existing databases: ```sql -- Grant ownership or full privileges on the database GRANT ALL PRIVILEGES ON DATABASE database_name TO databasement; -- Connect to the database and grant schema access \c database_name GRANT ALL PRIVILEGES ON SCHEMA public TO databasement; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO databasement; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO databasement; ``` :::note[Single database only] For single-database access without `CREATEDB`, the target database must already exist. Grant `ALL PRIVILEGES` on that specific database and its schema. The user won't be able to drop/recreate the database during restore - Databasement will drop and recreate tables instead. ::: ### Microsoft SQL Server SQL Server uses `sqlpackage` to extract and publish `.dacpac` files (schema + table data). Supports on-prem SQL Server 2017+ and Azure SQL Database (default port: 1433). Server-level objects (logins, users, permissions, role memberships) are excluded from the backup. The login needs `db_owner` on each target database, plus permission to drop and create databases (restore drops the target first). `sysadmin` works; on Azure SQL, use a server admin or grant `dbmanager` on `master`. :::info Cross-edition restore Restoring across editions (e.g. Azure SQL → on-prem) may fail if the source uses features the target doesn't support — a `sqlpackage` limitation. ::: ### MongoDB MongoDB uses the `mongodump` and `mongorestore` CLI tools for backup and restore operations. #### Connection settings | Field | Description | |-------|-------------| | Host | MongoDB server hostname or IP | | Port | MongoDB port (default: 27017) | | Username | Database user (optional for unauthenticated instances) | | Password | User password | | Auth Source | Authentication database (default: `admin`) | #### Creating a backup user ```javascript use admin db.createUser({ user: "databasement", pwd: "your_secure_password", roles: [ { role: "readAnyDatabase", db: "admin" }, { role: "backup", db: "admin" }, { role: "restore", db: "admin" } ] }) ``` :::note The `admin`, `local`, and `config` system databases are automatically excluded from the database list. ::: ### Redis / Valkey Redis and Valkey instances are backed up using `redis-cli --rdb`, which creates a point-in-time RDB snapshot of the entire dataset. #### Connection settings | Field | Description | |-------|-------------| | Host | Redis server hostname or IP | | Port | Redis port (default: 6379) | | Username | ACL username (optional, Redis 6+) | | Password | Server password or ACL user password | :::info Backup only Redis/Valkey supports backup only. Restore is not currently supported due to the nature of RDB file imports, which require direct server access. ::: ### SQLite SQLite databases are backed up by copying the database file directly. Databasement connects to the remote server via SFTP (through an SSH tunnel) to access the file. #### Connection settings | Field | Description | |-------|-------------| | Database paths | One or more absolute paths to `.sqlite` files on the remote server | :::note SQLite requires an SSH tunnel to access remote database files. Databasement uses SFTP over the tunnel to copy and restore files. ::: ### Firebird Firebird databases are backed up using the `gbak` CLI, which connects to the Firebird server over TCP and streams a transportable backup file. Each `.fdb` file on the server is its own database, so the connection is configured with both server credentials and one or more file paths. #### Connection settings | Field | Description | |-------|-------------| | Host | Firebird server hostname or IP | | Port | Firebird port (default: 3050) | | Username | Firebird user (default: `SYSDBA`) | | Password | Server password | | Database paths | One or more absolute paths to `.fdb` files on the server | #### Creating a backup user `SYSDBA` works out of the box. For a dedicated backup account: ```sql CREATE USER databasement PASSWORD 'your_secure_password'; GRANT RDB$ADMIN TO databasement; ``` :::note Restore replaces the target file Restore is performed with `gbak -rep`, which writes a fresh `.fdb` at the target path and replaces an existing file at that path if one is present. The user supplies the destination path during restore. ::: ## Browsing Data with Adminer Databasement can launch [Adminer](https://www.adminer.org/) directly against a registered server to inspect schema and run queries from the browser. Supported for **MySQL**, **PostgreSQL**, and **SQLite** servers that connect without an SSH tunnel. Enabled by default for Admins only. A Super Admin can change the minimum required role (Viewer, Member, or Admin) or disable the feature entirely under **Configuration → Application**. When enabled, a *Browse* action appears on compatible servers in the Database Servers list and opens Adminer pre-authenticated with the server's stored credentials. ## Troubleshooting Connection Issues ### Common Connection Issues | Error | Solution | |--------------------|------------------------------------------------------------| | Connection refused | Verify host, port, and that the database server is running | | Access denied | Check username and password | | Unknown host | Verify the hostname is correct and DNS is resolving | | Connection timeout | Check firewall rules and network connectivity | ### Docker Networking When running Databasement in Docker and connecting to databases in other containers, you need to ensure network connectivity between them. #### Containers in Different docker-compose Projects By default, each docker-compose project creates its own isolated network. To connect to a database in another project: **Option 1: Use an external network (recommended)** 1. Create a shared network: ```bash docker network create shared-db-network ``` 2. In your application database's `docker-compose.yml`, add the external network: ```yaml services: mysql: # ... your config networks: - default - shared-db-network networks: shared-db-network: external: true ``` 3. In Databasement's `docker-compose.yml`, add the same network: ```yaml services: app: # ... your config networks: - default - shared-db-network worker: # ... your config networks: - default - shared-db-network networks: shared-db-network: external: true ``` 4. Restart both projects and use the container name as the host (e.g., `mysql`). **Option 2: Connect to an existing network** Find the network name of your database container: ```bash docker network ls docker inspect | grep -A 20 "Networks" ``` Then connect Databasement to that network: ```yaml networks: other-project_default: external: true ``` #### Standalone Docker Containers (no docker-compose) For containers started with `docker run`: 1. Create a network if you don't have one: ```bash docker network create my-network ``` 2. Start your database container on that network: ```bash docker run -d --name mysql --network my-network mysql:8 ``` 3. Connect Databasement to the same network: ```bash docker network connect my-network databasement-app ``` 4. Use the container name (`mysql`) as the host in Databasement. #### Using Host Network Mode If your database is accessible on the host machine (e.g., installed directly or exposed via port mapping), you can use host network mode: ```yaml services: app: network_mode: host ``` Then use `localhost` or `127.0.0.1` as the host. Note that this disables Docker's network isolation. #### Connecting to Host Machine's Database If your database runs directly on the host machine (not in Docker): | Platform | Host to use | |---------------|------------------------------------------------| | Linux | `172.17.0.1` or `host.docker.internal` (Docker 20.10+) | | macOS/Windows | `host.docker.internal` | Example: If MySQL is running on your laptop on port 3306, use `host.docker.internal:3306`. ### Firewall Considerations Ensure your firewall allows connections: - **Docker networks**: Usually handled automatically - **Host firewall (iptables/ufw)**: May need rules for Docker bridge networks - **Cloud firewalls (AWS Security Groups, etc.)**: Add inbound rules for the database port ## SSH Tunnel Connect to databases that aren't directly reachable from Databasement — in private networks, behind a bastion/jump host, or on a remote Docker host whose database ports aren't exposed. Databasement opens the tunnel before each backup/restore and closes it afterward, with credentials encrypted at rest. See [SSH Tunnel](./ssh-tunnel.md) for configuration and for backing up databases on a remote host. --- ## Storage Volumes Storage volumes are the destinations where your backup files are stored. Databasement supports multiple storage backends to fit your infrastructure needs. ## Volume Types ### Local Storage Local volumes store backups on the filesystem where Databasement is running. This is the simplest option for single-server setups. | Field | Description | |-------|-------------| | **Path** | Absolute path to the backup directory | :::info Ensure the Databasement container has write access to the specified path. You may need to mount a volume when running Docker: ```bash docker run -v /path/on/host:/backups davidcrty/databasement ``` ::: ### S3 Storage S3 volumes store backups in AWS S3 or any S3-compatible object storage (MinIO, DigitalOcean Spaces, Backblaze B2, etc.). All credentials and settings are configured per-volume. | Field | Description | |-------|-------------| | **Bucket** | S3 bucket name | | **Prefix** | Optional path prefix within the bucket (e.g., `backups/production/`) | | **Region** | AWS region where the bucket is located (e.g., `us-east-1`) | | **Access Key ID** | AWS access key (optional — see below) | | **Secret Access Key** | AWS secret key (optional — see below) | **Advanced settings** (expand in the form): | Field | Description | |-------|-------------| | **Custom Endpoint** | For S3-compatible storage (MinIO, DigitalOcean Spaces, etc.) | | **Public Endpoint** | Public URL for presigned download links when the internal endpoint differs | | **Use Path-Style Endpoint** | Required for most S3-compatible providers (MinIO, etc.) | | **IAM Role ARN** | Assume this IAM role via STS before accessing S3 | | **Role Session Name** | Identifier for the assumed role session (default: `databasement`) | | **STS Endpoint** | Custom STS endpoint for role assumption | :::tip Credentials are optional. When left blank, the AWS SDK uses its default credential chain: environment variables, EC2/ECS instance roles, and EKS IRSA. This allows deployments on AWS infrastructure to work without explicit keys. ::: :::tip The secret access key is encrypted at rest in the database using Laravel's encryption. It is never stored in plain text. ::: ### SFTP Storage SFTP volumes store backups on a remote server via SSH File Transfer Protocol. This is ideal for storing backups on a dedicated backup server or NAS. | Field | Description | |-------|-------------| | **Host** | SFTP server hostname or IP address | | **Port** | SSH port (default: 22) | | **Username** | SSH username | | **Password** | SSH password | | **Root Directory** | Base path on the remote server (e.g., `/backups`) | | **Connection Timeout** | Timeout in seconds (default: 10) | :::note Only password authentication is currently supported. SSH key authentication is planned for a future release. ::: :::tip The password is encrypted at rest in the database using Laravel's encryption. It is never stored in plain text. ::: ### FTP Storage FTP volumes store backups on a remote FTP server. Both standard FTP and FTPS (FTP over SSL/TLS) are supported. | Field | Description | |-------|-------------| | **Host** | FTP server hostname or IP address | | **Port** | FTP port (default: 21) | | **Username** | FTP username | | **Password** | FTP password | | **Root Directory** | Base path on the remote server (e.g., `/backups`) | | **Enable SSL** | Use FTPS (FTP over SSL/TLS) for encrypted transfers | | **Passive Mode** | Use passive mode for data connections (recommended for most setups) | | **Connection Timeout** | Timeout in seconds (default: 90) | :::warning Standard FTP transmits credentials and data in plain text. Enable SSL for secure transfers, or prefer SFTP for better security. ::: :::tip The password is encrypted at rest in the database using Laravel's encryption. It is never stored in plain text. ::: ## Connection Testing Before saving a volume, use the **Test Connection** button to verify: - The storage location is accessible - Write permissions are configured correctly - Credentials are valid (for S3/SFTP/FTP) The test creates a small temporary file, reads it back to verify integrity, then deletes it. ## Volume Immutability Once a volume has backup snapshots associated with it, the storage configuration becomes read-only. This protects backup integrity by ensuring snapshots always point to their original storage location. You can still rename the volume, but the storage type and configuration cannot be changed. To use different storage settings, create a new volume. --- ## Backups Databasement allows you to create on-demand backups of your databases. Backups are processed asynchronously, so you can continue using the application while they run. ## How Backups Work When you create a backup, Databasement: 1. Connects to the database server (via SSH tunnel if configured) 2. Runs the appropriate dump command for the database type 3. Compresses the output with gzip 4. Transfers the compressed file to the selected storage volume 5. Creates a snapshot record with metadata ### Backup Commands Databasement uses native database tools for reliable backups: **MySQL/MariaDB:** ```bash mariadb-dump --routines --add-drop-table --complete-insert --hex-blob --quote-names --skip_ssl \ --host='...' --port='...' --user='...' --password='...' 'database_name' > dump.sql ``` **PostgreSQL:** ```bash PGPASSWORD='...' pg_dump --clean --if-exists --no-owner --no-privileges --quote-all-identifiers \ --host='...' --port='...' --username='...' 'database_name' -f dump.sql ``` **SQLite:** ```bash sqlite3 '/path/to/database.sqlite' ".backup 'dump.db'" ``` This creates a consistent snapshot and is safe for databases running in WAL mode. **Firebird:** ```bash gbak -b -g -user '...' -password '...' '/path/to/source.fdb' '/path/to/dump.fbk' ``` **MongoDB:** ```bash mongodump --host='...' --port='...' --username='...' --password='...' \ --authenticationDatabase='admin' --db='database_name' --archive=dump.archive ``` **Redis/Valkey:** ```bash redis-cli -h '...' -p '...' -a '...' --no-auth-warning --rdb dump.rdb ``` All dumps are then compressed with gzip before being transferred to the storage volume. ## Failed Backups If a backup fails, check: 1. **Database connectivity**: Can Databasement still connect to the server? 2. **Disk space**: Is there enough space on the storage volume? 3. **Permissions**: Does the database user have backup privileges? 4. **Timeout**: Large databases may need more time Failed backup reasons are logged and visible in the snapshot details. ## Retention Policies Retention policies control how long backups are kept before being automatically deleted. Databasement offers two retention strategies: ### Simple (Days-Based) The simplest option: keep all backups for a specified number of days. **Example:** With `retention_days = 30`, any backup older than 30 days is automatically deleted. | Day | Backups Kept | |-----|--------------| | 1-30 | All backups | | 31+ | Deleted | **Best for:** Short-term backup needs, development environments, or when storage cost isn't a concern. ### GFS (Grandfather-Father-Son) A tiered retention strategy that keeps recent backups for quick recovery while preserving older snapshots for long-term archival — without keeping every single backup. **How it works:** | Tier | What it keeps | Example | |------|---------------|---------| | **Daily** (Son) | The N most recent backups | Keep last 7 backups | | **Weekly** (Father) | 1 backup per week for N weeks | Keep 1/week for 4 weeks | | **Monthly** (Grandfather) | 1 backup per month for N months | Keep 1/month for 12 months | **Example with default values (7 daily, 4 weekly, 12 monthly):** After running daily backups for a year, you'll have approximately: - **7 recent backups** (last 7 days) - **4 weekly backups** (one from each of the past 4 weeks) - **12 monthly backups** (one from each of the past 12 months) **Total: ~23 backups** instead of 365 — saving significant storage while maintaining recovery points across the entire year. ``` Timeline visualization: Today ◄─────────────────────────────────────────────── 1 year ago │ │ ├── Daily ──┤ (7 backups) │ │ │ │ ├── Weekly ─────────┤ (4 backups) │ │ │ │ └── Monthly ─────────────────────────────────────────────┘ (12 backups) ``` **Best for:** Production environments, compliance requirements, or when you need long-term recovery options without excessive storage costs. :::info Per-Database Retention Retention policies are applied **per database**, not per server. If you backup 3 databases with GFS (7 daily), each database keeps its own 7 most recent backups — totaling 21 backups across all databases. ::: ### Choosing a Retention Policy | Scenario | Recommended Policy | |----------|-------------------| | Development/testing | Simple: 7-14 days | | Small production app | Simple: 30 days | | Business-critical data | GFS: 7d/4w/12m | | Compliance requirements | GFS: 14d/8w/24m | :::tip You can disable any GFS tier by leaving it empty. For example, setting only "Monthly: 12" keeps just one backup per month for a year. ::: ## Best Practices ### Before Production Backups 1. **Test the connection** before creating backups 2. **Verify restore** by testing a restore to a development server 3. **Monitor disk space** on your storage volumes ### Backup Sizing Compressed backup sizes vary, but as a rough guide: - A 1GB database typically compresses to 100-300MB - Text-heavy data compresses better than binary data ### Security Considerations - Use dedicated backup users with minimal required privileges - Store backups in secure, encrypted storage when possible - Regularly test your restore process --- ## Snapshots Snapshots are the backup files created when you backup a database. They contain all the data needed to restore your database to a specific point in time. ## File Verification Databasement verifies daily that backup files still exist on their storage volumes. Missing files are surfaced on the dashboard and in the jobs index with a "File missing" warning. You can also trigger verification manually from the dashboard. See [Backup Configuration](/self-hosting/configuration/backup) for `BACKUP_VERIFY_FILES` and `BACKUP_VERIFY_FILES_CRON` settings. ## Restore Process When you restore a snapshot, Databasement: 1. Downloads the snapshot from storage 2. Decompresses the backup file 3. Connects to the target database server 4. Drops and recreates the target database (if it exists) 5. Restores the data using native database tools ### Restore Commands **MySQL/MariaDB:** ```bash mariadb --host='...' --port='...' --user='...' --password='...' --skip_ssl \ 'database_name' -e "source /path/to/dump.sql" ``` **PostgreSQL:** ```bash PGPASSWORD='...' psql --host='...' --port='...' --username='...' \ 'database_name' -f '/path/to/dump.sql' ``` **SQLite:** ```bash cp '/path/to/snapshot' '/path/to/database.sqlite' ``` **Firebird:** ```bash gbak -rep -user '...' -password '...' '/path/to/dump.fbk' 'host/port:/path/to/target.fdb' ``` **MongoDB:** ```bash mongorestore --host='...' --port='...' --username='...' --password='...' \ --authenticationDatabase='admin' --archive='/path/to/dump.archive' \ --nsFrom='source_db.*' --nsTo='target_db.*' --drop ``` :::info Redis/Valkey restore is not currently supported. RDB file imports require direct server access. ::: All snapshots are decompressed with `gzip -d` before restore. ## Scheduled Restores You can replay the **latest completed snapshot** of a source database onto a target server on a recurring schedule — useful for keeping a staging or QA database refreshed from production. A scheduled restore is configured with: - **Source server** (and database name, unless the type is whole-instance like SQLite or Redis) - **Target server** and destination database name/path - **Schedule** — reuses the same cron schedules defined under Configuration → Backup On each run, Databasement picks the most recent completed snapshot of the source database and runs the normal [restore process](#restore-process) against the target. Source and target must be of the same database type. --- ## Organizations Databasement supports multi-organization setups, allowing you to isolate resources (database servers, volumes, agents, snapshots) between teams or projects. ## Concepts - Every resource belongs to exactly one organization - Users can belong to multiple organizations with different roles in each - A **default organization** is created automatically on first install - Super admins can create and manage additional organizations ## User Roles Roles are **per-organization**. A user can be an admin in one org and a viewer in another. | Role | Scope | |------------|--------------------------------------------------------------| | **Super Admin** | Global. Full access to all organizations and settings. | | **Admin** | Per-org. Can manage users and all resources within the org. | | **Member** | Per-org. Can manage resources but not users. | | **Viewer** | Per-org. Read-only access. | Super admin is a flag on the user account, not an org role. It grants access everywhere regardless of membership. ## Switching Organizations Use the organization switcher in the sidebar to change your active context. All pages (dashboard, servers, volumes, snapshots) reflect the selected organization. The switcher is visible when you belong to more than one organization or are a super admin. On switch, you are redirected to the dashboard to avoid stale resource URLs. Your selection is persisted in a cookie and restored on next login. ## Managing Organizations Super admins can manage organizations from **Configuration > Organizations**: - Create new organizations - Rename existing organizations - Delete empty organizations (all servers, volumes, and agents must be removed first) The default organization cannot be renamed or deleted. Organization names must be unique. ## User Management The **Users** page shows members of the currently selected organization. Admins (org or super) can add users from the **Users > Add User** page: - **Invite new user** — Creates a new account with an invitation link. The user joins the current organization with the selected role. - **Add existing user** — Adds a user who already has an account (in another org) to the current organization. Select the user and assign a role. **Delete** permanently removes the user account. **Remove from org** detaches the user from the current organization but keeps their account for other orgs. | Scenario | Org Admin | Super Admin | |---|---|---| | User in **one** org | Delete | Delete | | User in **multiple** orgs | Remove from org | Delete or Remove from org | - Admins cannot delete or remove themselves - Org admins cannot act on super admin users - The last super admin cannot be deleted ## API Usage API requests target an organization using the `?org_id=` query parameter (by organization ID): ``` GET /api/v1/database-servers?org_id=01JA2B3C4D5E6F7G8H9J0KMNPQ ``` Alternatively, pass the `X-Organization-Id` header with the same value. If neither is provided, the default organization is used. You can find the organization ID in **Configuration > Organizations**. ## Data Isolation Each organization has its own: - Database servers and SSH configs - Storage volumes - Snapshots and backup jobs - Agents Shared across all organizations: - User accounts and API tokens - Global configuration (application, authentication, backup, notification settings) - Backup schedules A backup's database server and volume must belong to the same organization. Cross-org restore is not allowed — the source snapshot and target server must be in the same org. ## Agents Agents belong to a single organization. They only execute jobs for servers within their assigned organization. ## OAuth / OIDC Auto-created OAuth users join the default organization by default. Set the `OAUTH_DEFAULT_ORGANIZATION_ID` environment variable to assign them to a different org instead. ## CLI Commands Artisan commands (`backup:run`, `cleanup:snapshots`, `verify:files`) operate globally across all organizations. They bypass org scoping by design. ## Fresh Install On first install: 1. A **Default** organization is created automatically 2. The first registered user becomes a super admin, attached to the default org as admin 3. Additional organizations can be created from Configuration --- ## Permissions Databasement uses a role-based access control system. Roles are assigned **per organization** — a user can have different roles in different organizations. See [Organizations](./organizations.md) for details on multi-org setup. ## User Roles | Role | Scope | Description | |-----------------|--------|----------------------------------------------------------------------------| | **Super Admin** | Global | Full access to all organizations, user deletion, and global configuration | | **Admin** | Org | Full access within the org, including user management | | **Member** | Org | Can manage database servers, volumes, and backups, but cannot manage users | | **Viewer** | Org | Read-only access to view resources and monitor backup status | ## Permissions by Resource ### Database Servers | Action | Viewer | Member | Admin | |-------------------|:------------:|:------------:|:-----:| | View list | ✅ | ✅ | ✅ | | Create | ❌ | ✅ | ✅ | | Edit | ❌ | ✅ | ✅ | | Delete | ❌ | ✅ | ✅ | | Run backup | ❌ | ✅ | ✅ | | Restore to server | ❌ | ✅ | ✅ | | Open Adminer | configurable | configurable | ✅ | Adminer access is enabled by default for Admins only. A Super Admin can change the threshold or disable the feature under **Configuration → Application**. See [Browsing Data with Adminer](./database-servers.md#browsing-data-with-adminer). ### Volumes | Action | Viewer | Member | Admin | |-----------|:------:|:------:|:-----:| | View list | ✅ | ✅ | ✅ | | Create | ❌ | ✅ | ✅ | | Edit | ❌ | ✅ | ✅ | | Delete | ❌ | ✅ | ✅ | ### Snapshots | Action | Viewer | Member | Admin | |--------------|:------:|:------:|:-----:| | View list | ✅ | ✅ | ✅ | | View details | ✅ | ✅ | ✅ | | Download | ❌ | ✅ | ✅ | | Delete | ❌ | ✅ | ✅ | ### Users | Action | Viewer | Member | Admin | |------------------------------|:------:|:------:|:-----:| | View list | ✅ | ✅ | ✅ | | Invite new user | ❌ | ❌ | ✅ | | Edit user role | ❌ | ❌ | ✅ | | Delete user | ❌ | ❌ | ✅* | | Remove from organization | ❌ | ❌ | ✅ | | Copy invitation link | ❌ | ❌ | ✅ | \* Org admins can only delete users who belong to their organization and no other. See restrictions below. ## Special Rules ### User Deletion **Super admins** can delete any user, with these restrictions: - Cannot delete yourself - Cannot delete the last super admin **Org admins** can delete a user only when all of the following are true: - The target user is not a super admin - The target user belongs to the admin's current organization - The target user belongs to **only one organization** (the admin's org) If the target user belongs to multiple organizations, admins can **remove them from the organization** instead of deleting them entirely. --- ## REST API Databasement provides a REST API for managing database servers, backups, snapshots, and storage volumes programmatically. ## Authentication All API requests require a Bearer token. Create one from **Settings → API Tokens** in the Databasement UI. Include the token in the `Authorization` header: ``` Authorization: Bearer YOUR_TOKEN ``` ## API Documentation Interactive API documentation is available at `/docs/api` on your Databasement instance. It is auto-generated from the codebase using [Scramble](https://scramble.dedoc.co/) and includes a "Try It" feature for testing endpoints directly. --- ## MCP Server Databasement includes a built-in [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that lets AI assistants manage your database backups through natural language. ## What is MCP? MCP is an open protocol that allows AI clients (Claude Code, Cursor, VS Code Copilot, etc.) to discover and call tools exposed by your application. Instead of clicking through the UI, you can say things like: - "List my database servers" - "Back up the production MySQL server" - "Restore the latest snapshot to staging" The MCP server wraps the same services that power the web UI and REST API, so behavior is consistent across all interfaces. ## Available Tools | Tool | Description | Destructive? | |------|-------------|:---:| | **list-database-servers** | List all registered servers with connection details and backup config. Optionally filter by database type. | No | | **list-snapshots** | List backup snapshots, optionally filtered by server. Returns most recent first. | No | | **trigger-backup** | Trigger an on-demand backup for a server. Returns snapshot IDs and job IDs for status tracking. | No | | **trigger-restore** | Restore a snapshot to a target server. Drops and recreates the target database. | **Yes** | | **get-job-status** | Check the status of a backup or restore job (pending, running, completed, failed). | No | ## Setup The MCP server is available at `/mcp`, protected by [Sanctum](https://laravel.com/docs/sanctum) token authentication. ### 1. Create an API Token Go to **Settings → API Tokens** in the Databasement UI and create a new token. ### 2. Configure Your AI Client Add the following to your MCP client configuration (e.g., `~/.claude/settings.json` or `.mcp.json` for Claude Code, `mcp.json` for Cursor): ```json { "mcpServers": { "databasement": { "command": "npx", "args": [ "mcp-remote", "https://your-databasement-instance.com/mcp", "--header", "Authorization: Bearer YOUR_SANCTUM_TOKEN" ] } } } ``` Replace the URL with your Databasement instance address and the token with the one generated in step 1. Tools will appear natively in your AI client (e.g., as `mcp__databasement__trigger-backup-tool` in Claude Code). :::note [mcp-remote](https://www.npmjs.com/package/mcp-remote) requires Node.js to be installed. It bridges the HTTP transport to stdio, which is supported by all MCP clients. ::: --- ## Development Guide This guide covers setting up a local development environment for contributing to Databasement. ## Requirements - PHP 8.4+ - Composer - Node.js 20+ & npm - Docker & Docker Compose ## Quick Start ### 1. Clone and Setup ```bash git clone https://github.com/David-Crty/databasement.git cd databasement make setup ``` This will: - Install Composer dependencies - Run database migrations - Install npm dependencies - Build frontend assets You should be ready to go! Open http://localhost:2226 in your browser to view the app. ### 2. Start Development Environment ```bash make start ``` This starts all Docker services: - **php** — FrankenPHP server on http://localhost:2226 - **queue** — Queue worker for async backup/restore jobs - **mysql** — MySQL 8.0 on port 3306 - **postgres** — PostgreSQL 16 on port 5432 Test database credentials: `admin` / `admin` / `testdb` ## Available Commands All PHP commands run through Docker. Use the Makefile targets or `docker compose exec app `. ### Testing ```bash make test # Run all tests make test-filter FILTER=ServerTest # Run specific tests make test-coverage # Run with coverage report ``` ### Code Quality ```bash make lint-fix # Auto-fix code style with Laravel Pint make lint-check # Check code style without fixing make phpstan # Run PHPStan static analysis ``` ### Database ```bash make migrate # Run pending migrations make migrate-fresh # Drop all tables and re-migrate make migrate-fresh-seed # Fresh migration with seeders make db-seed # Run database seeders ``` ### Assets ```bash npm run build # Build production assets npm run dev # Start Vite dev server (HMR) make build # Alternative: build via Makefile ``` ### Docker Services ```bash make start # Start all services docker compose logs -f # View logs from all services docker compose logs -f queue # View queue worker logs docker compose restart queue # Restart queue worker docker compose down # Stop all services ``` ## OAuth / SSO Testing Databasement includes a [Dex](https://dexidp.io/) OIDC server for local OAuth testing. The Dex service is commented out by default in `docker-compose.yml`. ### 1. Add Hosts Entry The Dex server uses a custom hostname that must resolve both from your browser and from within Docker containers: ```bash echo "127.0.0.1 dex-local" | sudo tee -a /etc/hosts ``` ### 2. Enable Dex Service Uncomment the `dex` service in `docker-compose.yml`: ### 3. Start Dex ```bash docker compose up dex -d ``` ### 4. Configure Environment Add to your `.env.local`: ```env OAUTH_OIDC_ENABLED=true OAUTH_OIDC_CLIENT_ID=databasement OAUTH_OIDC_CLIENT_SECRET=databasement-secret OAUTH_OIDC_BASE_URL=http://dex-local:5556/dex OAUTH_OIDC_LABEL=SSO ``` ### Test User | Email | Password | |-------|----------| | `user@databasement.com` | `databasement` | ### Testing Flow 1. Ensure hosts entry is added (step 1) 2. Start Dex: `docker compose up dex -d` 3. Visit the login page at `http://localhost:2226/login` 4. Click "Continue with SSO" 5. Enter test credentials: `user@databasement.com` / `databasement` 6. You'll be redirected back and logged in ### OAuth Behavior Notes - **New users**: Created automatically with the role defined by `OAUTH_DEFAULT_ROLE` - **Existing users**: When an existing user logs in via OAuth (matching email), their account is linked and their password is cleared - **OAuth-only users**: Cannot use password login — they must use the OAuth button - **Settings access**: OAuth-only users don't see Password or Two-Factor settings (managed by the OAuth provider) ## Git Hooks Pre-commit hooks (via Husky) automatically run: 1. `make lint-fix` — Auto-format code with Laravel Pint 2. `make test` — Run all Pest tests Ensure tests pass before committing. ## Architecture Overview ### Tech Stack | Layer | Technology | |-------|------------| | Backend | Laravel 12, PHP 8.4+ | | Frontend | Livewire, Mary UI, daisyUI, Tailwind CSS 4 | | Testing | Pest PHP | | Database | SQLite (dev), supports MySQL/PostgreSQL/MariaDB | | Auth | Laravel Fortify with 2FA support | ### Key Models - **DatabaseServer** — Database connection configurations - **Volume** — Storage destinations (local, S3) - **Backup** — Backup configurations (schedule, retention, volume) - **Snapshot** — Individual backup snapshots with metadata - **BackupJob** — Tracks backup/restore job execution and logs ### Key Services - **BackupTask** — Executes database dumps, compression, and storage - **RestoreTask** — Downloads, decompresses, and restores snapshots - **DatabaseProvider** — Creates database handlers, tests connections, lists databases - **SshTunnelService** — Establishes SSH tunnels for database connections through bastion hosts - **ShellProcessor** — Executes shell commands with logging ### Livewire Components - `DatabaseServer/*` — CRUD operations for database servers - `Volume/*` — CRUD operations for storage volumes - `BackupJob/Index` — Job listing with logs modal - `Snapshot/Index` — Snapshot listing and management - `Settings/*` — User settings (Profile, Password, TwoFactor) - `RestoreModal` — 3-step wizard for snapshot restoration ### Backup & Restore Workflow **Backup Process:** 1. Connect to database server 2. Establish SSH tunnel if configured 3. Execute database-specific dump (mariadb-dump/pg_dump/sqlpackage/mongodump/redis-cli/cp) 4. Compress with gzip 5. Upload to configured volume (local/S3) 6. Record snapshot metadata **Restore Process:** 1. Select source snapshot 2. Download and decompress 3. Validate compatibility (database types must match) 4. Drop and recreate target database 5. Restore SQL dump **Cross-Server Restore:** Restore production snapshots to staging/preprod as long as database types match. ## Configuration ### Environment Variables The `.env` file is committed to the repository and contains default development configuration. To override these values, create a `.env.local` file (which is gitignored). Key development configuration: ```env # Application APP_URL=http://localhost:2226 # Database (for application data) DB_CONNECTION=sqlite # Queue QUEUE_CONNECTION=database # AWS S3 (optional, for S3 volume testing) AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 ``` ## Testing Strategy We use Pest PHP for testing. Key principles: - **Test business logic and behaviors** — Not framework internals - **Mock external services** — AWS SDK, S3 client, etc. - **Don't mock models** — Use real database interactions ### What to Test - Authorization (who can access what) - Business logic (backup works, restore works, cleanup deletes correct snapshots) - Integration points (external services, commands) ### What NOT to Test - Form validation rules (Laravel handles this) - Eloquent relationships and cascades - Session flash messages - Framework behavior ## Submitting Changes 1. Create a feature branch from `main` 2. Write tests for new functionality 3. Ensure all tests pass: `make test` 4. Run code quality checks: `make lint-fix && make phpstan` 5. Submit a pull request with a clear description For significant changes, open an issue first to discuss the approach. --- ## SSH Tunnel Connect to databases that aren't directly reachable from Databasement: in private networks, behind a bastion/jump host, or on a remote Docker host whose database ports aren't published. :::info How it works Databasement runs `ssh -N -L ::` before each backup or restore and closes it afterward. SSH credentials are encrypted at rest. ::: :::note No database tools needed on the SSH host The dump and restore tools (`mariadb-dump`, `pg_dump`, `mongodump`, and so on) run inside the Databasement container and are NOT NEEDED on the SSH host. ::: ## Configuration Enable **SSH Tunnel** on the database server and point it at the SSH host: | Field | Description | |-------|-------------| | SSH Host | SSH server hostname or IP (bastion, jump host, or remote Docker host) | | SSH Port | SSH port (default: 22) | | SSH Username | SSH user | | Auth Type | `Password` or `Private Key` (with optional passphrase) | ### Reusing an SSH configuration Once you've saved an SSH config, the form offers **Use existing** (pick a saved config) or **Set up a new SSH config**. Reusing one lets several database servers share the same host connection — update it once and every server that references it follows. ### Generating an SSH key With **Auth Type** set to **Private Key**, the form can **Generate a new Ed25519 keypair** for you. Copy the displayed public key into `~/.ssh/authorized_keys` on the SSH server before saving — it's shown only once and the public key isn't stored. ## Backing up databases on a remote host When databases run in Docker containers on a remote machine with ports only on an internal network, the tunnel reuses the **same SSH access you already use to manage that host** — no separate agent is needed, and the database stays private. Publish the database port to the host's loopback, so it's reachable from the host but not from the network: ```yaml services: db: image: postgres:16 ports: - "127.0.0.1:5432:5432" ``` Set the database **Host** to `127.0.0.1` and **Port** to `5432`. Loopback is exactly where the tunnel terminates, so Databasement can reach the database while the network cannot. ### Optional: SSH server in the stack If the host doesn't expose SSH — or you'd rather not publish the database port at all — add a small SSH server to the same Compose project. Only the SSH port is published; Databasement reaches the database by its service name over the project's default network. ```yaml services: db: image: postgres:18 sshd: image: lscr.io/linuxserver/openssh-server environment: USER_NAME: databasement PUBLIC_KEY: "ssh-ed25519 AAAA... databasement-tunnel" DOCKER_MODS: linuxserver/mods:openssh-server-ssh-tunnel ports: - "2222:2222" # only SSH is published — not the database ``` Point the SSH Tunnel at the host on port `2222`, then set the database **Host** to `db` and **Port** to `5432` — the service name resolves on the project's default network. For same-host containers sharing a Docker network (no SSH), see [Docker Networking](./database-servers.md#docker-networking) instead. ## Security :::info Keep the database private What matters most is that the database is not reachable from the public internet: either because its host is on a private network, or because you bound the port to loopback (`127.0.0.1:5432:5432`). ::: Optionally, restrict the SSH key to forwarding only in `authorized_keys`: ``` restrict,port-forwarding ssh-ed25519 AAAA... databasement-tunnel ```