---
title: "Deploy MariaDB 12.3 LTS with phpMyAdmin and Backups"
description: "MySQL-compatible MariaDB LTS, tuned, with phpMyAdmin and daily backups."
category: "Storage"
url: https://railway.com/deploy/mariadb-123-lts-with-phpmyadmin-and-back
---

# Deploy MariaDB 12.3 LTS with phpMyAdmin and Backups

MySQL-compatible MariaDB LTS, tuned, with phpMyAdmin and daily backups.

**[Deploy MariaDB 12.3 LTS with phpMyAdmin and Backups on Railway](https://railway.com/template/mariadb-123-lts-with-phpmyadmin-and-back)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/mariadb-123-lts-with-phpmyadmin-and-back/manifest.json

- **Creator:** bento
- **Category:** Storage
- **Total deploys:** 1

## Template content

### MariaDB Backup https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/rclone.svg

- **Source:** baranberkay96/mariadb-railway

### phpMyAdmin https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/phpmyadmin.svg

- **Image:** phpmyadmin:5.2.3-apache
- **Start command:** `/bin/bash -c 'set -e; n() { case "$1" in ""|*[!0-9]*) echo $2 ;; *) echo $1 ;; esac; }; E=/etc/apache2/mods-enabled; N=$(ls $E/mpm_*.load 2>/dev/null | wc -l | tr -d " "); if [ "$N" -ne 1 ] || [ ! -e $E/mpm_prefork.load ]; then echo "pma-start: $N Apache MPM modules enabled, keeping mpm_prefork only"; rm -f $E/mpm_*.load $E/mpm_*.conf; a2enmod -q mpm_prefork; fi; W=$(n "$PMA_APACHE_WORKERS" 10); if [ $W -lt 2 ]; then W=2; fi; if [ $W -gt 256 ]; then W=256; fi; A=/etc/apache2/conf-enabled/zz-railway.conf; echo "ServerTokens Prod" > $A; echo "ServerSignature Off" >> $A; echo "<IfModule mpm_prefork_module>" >> $A; echo "StartServers 2" >> $A; echo "MinSpareServers 1" >> $A; echo "MaxSpareServers 4" >> $A; echo "ServerLimit $W" >> $A; echo "MaxRequestWorkers $W" >> $A; echo "MaxConnectionsPerChild 500" >> $A; echo "</IfModule>" >> $A; I="${PHP_INI_DIR:-/usr/local/etc/php}/conf.d/zz-railway.ini"; echo "display_errors = Off" > $I; echo "log_errors = On" >> $I; echo "expose_php = Off" >> $I; echo "pma-start: Apache prefork MaxRequestWorkers $W, PMA_HOST ${PMA_HOST:-unset}:${PMA_PORT:-3306}, root login ${PMA_ALLOW_ROOT:-false}"; exec /docker-entrypoint.sh apache2-foreground' pma-start`
- **Health check:** /
- **Public domain:** Yes

### MariaDB https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/mariadb.svg

- **Image:** mariadb:12.3.3
- **Start command:** `/bin/bash -c 'set -e; F=/etc/mysql/conf.d/zz-railway.cnf; X=/tmp/zz-railway.cnf; k() { v=$1; if [ $v -lt $2 ]; then v=$2; fi; if [ $v -gt $3 ]; then v=$3; fi; echo $v; }; n() { case "$1" in ""|*[!0-9]*) echo $2 ;; *) echo $1 ;; esac; }; w() { echo "$1 = $2" >> $X; }; echo "# Railway template: rewritten at every boot from the DB_* variables; DB_EXTRA_ARGS overrides it" > $X; echo "[mariadbd]" >> $X; COL="${DB_COLLATION:-utf8mb4_unicode_ci}"; case "$COL" in utf8mb4_) COL=bad ;; utf8mb4_*[!a-z0-9_]*) COL=bad ;; utf8mb4_*) ;; *) COL=bad ;; esac; if [ "$COL" = bad ]; then echo "mariadb-tune: DB_COLLATION=${DB_COLLATION} is not a utf8mb4 collation name, using utf8mb4_unicode_ci"; COL=utf8mb4_unicode_ci; fi; w character_set_server utf8mb4; w collation_server $COL; if [ "$COL" != utf8mb4_uca1400_ai_ci ]; then w character_set_collations utf8mb4=$COL; fi; w max_allowed_packet 64M; w innodb_use_native_aio 0; w ignore_db_dirs lost+found; case "${DB_SLOW_QUERY_LOG:-off}" in on|ON|On|true|TRUE|True|yes|1) SLOW=on ;; *) SLOW=off ;; esac; if [ $SLOW = on ]; then LQT="${DB_LONG_QUERY_TIME:-2}"; case "$LQT" in ""|.|*[!0-9.]*|*.*.*) LQT=2 ;; esac; w log_output TABLE; w log_slow_query ON; w log_slow_query_time $LQT; fi; if [ "${DB_TUNING:-auto}" = off ]; then echo "mariadb-tune: DB_TUNING=off, MariaDB defaults apply to memory and connections"; else H=$(( $(n "$(grep -m 1 MemTotal /proc/meminfo | tr -s " " | cut -d " " -f 2)" 1048576) / 1024 )); L=$(cat /sys/fs/cgroup/memory.max 2>/dev/null || cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null || echo max); M=$(( $(n "$L" $((H * 1048576))) / 1048576 )); if [ $M -gt $H ]; then M=$H; fi; B=$(k $(n "$DB_MEMORY_LIMIT_MB" $(k $M 256 8192)) 256 4194304); P=$(k $(n "$DB_BUFFER_POOL_MB" $(k $((B / 2)) 128 2097152)) 32 4194304); C=$(k $(n "$DB_MAX_CONNECTIONS" $(k $((B / 8)) 100 500)) 10 100000); T=$(k $((B / 64)) 16 64); w innodb_buffer_pool_size ${P}M; w innodb_log_file_size $(k $((P / 4)) 96 512)M; w max_connections $C; w tmp_table_size ${T}M; w max_heap_table_size ${T}M; w innodb_io_capacity 1000; w innodb_io_capacity_max 2000; echo "mariadb-tune: budget ${B}MB (container ${M}MB, host ${H}MB)"; fi; mkdir -p /etc/mysql/conf.d; install -m 644 $X $F; echo "mariadb-tune: $(grep -v "^#" $X | paste -sd " " -)"; set -f; exec docker-entrypoint.sh "$@" ${DB_EXTRA_ARGS:-}' mariadb-start mariadbd`

## Buckets

- **Bucket**

## Documentation

# Deploy and Host MariaDB with Railway

[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/mariadb-123-lts-with-phpmyadmin-and-back?referralCode=20H68q)

MariaDB is an open-source relational database that speaks the MySQL protocol and SQL dialect. This community template runs MariaDB 12.3 LTS on Railway with Railway-MySQL-compatible connection variables, memory-aware tuning, a pre-connected phpMyAdmin web UI and daily verified backups to a Railway Bucket.

## About Hosting MariaDB

A production MariaDB needs more than the container. The InnoDB buffer pool should match the memory the service actually has, the character set should work with every MySQL driver your apps use, the admin UI must not become an open door, and the data needs copies outside the volume. This template pins the 12.3 long-term-support release, sizes the buffer pool, redo log and connection limit from the container's memory limit at every boot, uses utf8mb4 with a MySQL-compatible collation, listens on IPv4 and IPv6, and exposes the same variable names as Railway's MySQL, so apps written for it connect without code changes. A cron job writes compressed, read-back-verified `mariadb-dump` backups with retention.

## Common Use Cases

- A MySQL-compatible database for WordPress, Laravel, Drupal, Strapi, Directus, Nextcloud or your own app
- Moving an app off Railway MySQL to an open-source GPL database with a long support window
- A shared SQL server for several services, managed through phpMyAdmin
- Development and staging databases that can be reset from yesterday's backup

## Dependencies for MariaDB Hosting

- MariaDB Server 12.3 (included: official `mariadb:12.3.3` image)
- phpMyAdmin 5.2 (included: official `phpmyadmin:5.2.3-apache` image)
- A Railway Bucket for backups (included, created by the template)

### Deployment Dependencies

- MariaDB release and maintenance policy: https://mariadb.org/about/
- MariaDB Docker image: https://hub.docker.com/_/mariadb (source: https://github.com/MariaDB/mariadb-docker)
- mariadb-dump: https://mariadb.com/docs/server/clients-and-utilities/backup-restore-and-import-clients/mariadb-dump
- phpMyAdmin documentation: https://docs.phpmyadmin.net/en/latest/ (image: https://hub.docker.com/_/phpmyadmin)
- Railway TCP Proxy: https://docs.railway.com/networking/tcp-proxy
- Railway Storage Buckets: https://docs.railway.com/storage-buckets
- Railway cron jobs: https://docs.railway.com/cron-jobs

### Implementation Details

| Service | Image | Purpose |
|---|---|---|
| MariaDB | `mariadb:12.3.3` | MariaDB 12.3 LTS, volume at `/var/lib/mysql`, private port 3306 (IPv4 + IPv6), TCP proxy for external clients |
| phpMyAdmin | `phpmyadmin:5.2.3-apache` | Web UI on a public HTTPS domain, connected to MariaDB over the private network |
| MariaDB Backup | built from this repo (`services/backup`) | Daily cron (03:30 UTC): accounts + every database with `mariadb-dump`, zstd, verified, 14-day retention (minimum 3 runs kept) |
| Bucket | Railway Bucket | Private S3 storage for the backups |

**Connection variables** (on the MariaDB service, reference them as `${{MariaDB.NAME}}`):

- `MYSQL_URL`: private URL, same name and format as Railway MySQL, e.g. `DATABASE_URL=${{MariaDB.MYSQL_URL}}`
- `MYSQL_PUBLIC_URL`: from outside Railway through the TCP proxy (egress is billed)
- `MYSQLHOST`, `MYSQLPORT`, `MYSQLUSER`, `MYSQLPASSWORD`, `MYSQLDATABASE`, `MYSQL_DATABASE`, `MYSQL_ROOT_PASSWORD`: Railway MySQL's names
- `MARIADB_URL`, `MARIADB_PUBLIC_URL` (`mariadb://` scheme), `MARIADB_USER`, `MARIADB_PASSWORD`, `MARIADB_DATABASE`, `MARIADB_ROOT_PASSWORD`

One difference to Railway MySQL: the URLs log in as the app user `railway` (all privileges on the `railway` database), not as root. If an app must create its own databases, set `MYSQLUSER=root` and `MYSQLPASSWORD=${{MARIADB_ROOT_PASSWORD}}` on the MariaDB service; the URLs follow.

**First login**

1. Open the phpMyAdmin service's domain.
2. Log in with `MARIADB_USER` (`railway`) and `MARIADB_PASSWORD` from the MariaDB service's Variables tab.
3. Point your app at `${{MariaDB.MYSQL_URL}}`, or connect from your machine with `mariadb --host={proxy domain} --port={proxy port} --user=railway --password railway`.

**Security defaults**: every password is generated at deploy. phpMyAdmin only talks to this MariaDB (no free-text server field), refuses empty passwords, has no setup page, and does not accept the root account unless you set `PMA_ALLOW_ROOT=true` on the phpMyAdmin service (turn it back off when done). Failed logins are written to the phpMyAdmin log. The TCP proxy is on for drop-in compatibility; remove it under MariaDB → Settings → Networking for a private-only database.

**Tuning**: at every boot the start command writes `zz-railway.cnf` and logs it: buffer pool = half the memory limit (auto budget capped at 8 GB), redo log, `max_connections` and temp tables scale with it, `max_allowed_packet` is 64 MB and the collation is `utf8mb4_unicode_ci`. Override with `DB_MEMORY_LIMIT_MB`, `DB_BUFFER_POOL_MB`, `DB_MAX_CONNECTIONS`, `DB_COLLATION` or any server option in `DB_EXTRA_ARGS` (e.g. `--sql-mode=TRADITIONAL`). `DB_SLOW_QUERY_LOG=on` logs queries slower than `DB_LONG_QUERY_TIME` seconds to the `mysql.slow_log` table. Buffer pool memory is used only as data is read.

**Backups and restore**: dumps land in the Bucket under `mariadb/{timestamp}/` with a `MANIFEST.txt`. Trigger one now with `railway cron run --service "MariaDB Backup"`. To restore, download `{database}.sql.zst` from the Bucket's Files tab and run, with a MariaDB client: `zstd -dc railway.sql.zst | mariadb --host={proxy domain} --port={proxy port} --user=root --password`. The dump recreates its database and tables; `users.sql.zst` recreates accounts on a fresh server. Use the `mariadb` client, not MySQL's `mysql`, which stops at the dump's first line.

**Scaling**: raise the MariaDB service's memory and CPU, then set `DB_MEMORY_LIMIT_MB` to match so the buffer pool grows. Raise `DB_MAX_CONNECTIONS` for many app instances. Hobby volumes start at 5 GB; Pro volumes at 50 GB and can be resized. MariaDB runs as one primary; phpMyAdmin and the backup job do not need scaling.

**Versions and upgrades**: all images are pinned. To move to a newer MariaDB release, change the tag on the MariaDB service and in `services/backup/Dockerfile` together; `MARIADB_AUTO_UPGRADE=1` backs up the system tables and runs `mariadb-upgrade` on the next boot. Take a backup first. phpMyAdmin upgrades by changing its tag.

**Cost**: roughly $3 per month idle and about $16 per month for a few GB of data with daily backups at Railway's usage prices.

### Why Deploy MariaDB on Railway?

Railway runs the database, its admin UI and the backup job together on a private network with persistent volumes and managed buckets, so you get a tuned MariaDB with backups in a few minutes without operating servers. The connection variables match Railway MySQL, and the database grows by raising the service limits and the volume size.


## Similar templates

- [RustFS | (Just Updated) S3 Object Store, Public URL and Keys Set](https://railway.com/deploy/rustfs-or-just-updated-s3-object-store-p) — S3-compatible object store, keys and public URL preset, volume kept
- [TimescaleDB](https://railway.com/deploy/timescaledb-railway-template) — A PostgreSQL-based time-series database for analytics on Railway.
- [Garage S3 Storage](https://railway.com/deploy/garage-s3-storage) — Ultra-light S3 server: fast, open-source, plug-and-play.

Open this page in a browser: https://railway.com/deploy/mariadb-123-lts-with-phpmyadmin-and-back
