MySQL
MySQL 8 or MariaDB databases on the VPS with their own TLS-only account, grants on its database only and IP-based external access.
- Category: Servers and infrastructure
- Status: Available
- Requirements: An active VPS with the
mysqlfeature available; If the engine already exists: MySQL 8.x or MariaDB 10.4 or later; Being a member with access to the VPS to install; admin to reveal, open access or delete data - MCP tools (Read only):
ver_catalogo_apps,ver_preflight_app,ver_app,ver_acceso_app,ver_operacion_app,listar_motores_db_vps,listar_bases_db_vps - MCP tools (Write or modify):
instalar_app,revelar_credencial_app,cambiar_lista_ip_app,reintentar_operacion_app - MCP tools (Destructive):
revocar_acceso_app,desinstalar_app
What can you do with it?
Every MySQL database is an app with its own account. The engine (MySQL or MariaDB) is one per VPS and shared by its databases; if it does not exist, MySQL is installed from the distribution packages.
- Create several
utf8mb4databases, each with its account and grants. - Reveal user, password and connection URI.
- Open the engine to the internet only for certain IPs, with TLS.
- From the VPS MySQL tab: databases, users, grants and tables.
When should you use it?
- Applications that expect MySQL (PHP, Laravel, many third-party tools).
- A WordPress site uses this same engine, with its own database and account per site.
How does it work in ArduMaker?
- Engine: if the agent reports it is not installed, it is installed once (
mysql-serverandmysql-client); if it exists, it is never reinstalled nor upgraded. - Variant:
mysql --versionis read. Only MySQL 8.x or MariaDB 10.4+; anything else fails withmotor_no_soportadowithout running SQL. app<id>_<hex>credential with a random password, stored encrypted before touching the VPS.- It logs in as administrator through the socket, with the password in an ephemeral options file, never on the command line.
- Account
'app<id>_<hex>'@'127.0.0.1'withREQUIRE SSL, andGRANT ALL PRIVILEGESonly on<db>.*, withoutGRANT OPTION, before creating the database. CREATE DATABASE <db> CHARACTER SET utf8mb4only if it did not exist.- Verification: TLS required, no global privileges and grants on a single database, its own.
The account is @'127.0.0.1' on purpose: not % (it would open the account to any origin) nor localhost (socket, no TLS). When the server lacks partial_revokes (or is MariaDB), the _ in the name is escaped in the GRANT, because there it is a wildcard.
config.external_access decides whether the database is reachable from the internet: {"ips": [...]} opens it only to those IPs; {"local": true} (or false) keeps it inside the VPS. If omitted, the dashboard uses the installer's IP; over MCP the agent's IP does not count, so without ips the database stays local instead of failing.
What actually changes?
| What | Detail |
|---|---|
| Engine | MySQL or MariaDB listening on 127.0.0.1:3306 |
| Database | utf8mb4; the same name with different case counts as the same |
| Account | 'app<id>_<hex>'@'127.0.0.1' REQUIRE SSL |
| TLS on MySQL 8 | SET PERSIST + ALTER INSTANCE RELOAD TLS, no restart |
| TLS on MariaDB | 90-ardumaker-tls.cnf file; the first time requires restarting the engine |
Internet access and IP allowlist
By default the engine listens only inside the VPS (3306 on 127.0.0.1). To use the database from outside, ArduMaker publishes an endpoint of the engine: db-<VPS id> inside the platform zone (direct DNS record, no proxy), on a reserved port between 61000 and 64999. On the host, a proxy carries that port to the VPS 3306 and the firewall lets in only the IPs on the list.
The certificate belongs to the db-<id> name and mysqld terminates it: the host proxy only passes TCP. The app account requires SSL wherever it comes from.
| List rule | Detail |
|---|---|
| Format | IPv4 or IPv6, with or without prefix; a single IP is /32 or /128. No names, loopback, link-local or multicast |
| Width | Minimum prefix /24 for IPv4 and /48 for IPv6 by default; 0.0.0.0/0 and ::/0 are rejected in the list |
| Size | Up to 20 entries; empty = closed to everyone |
| Host names | Up to 5 (allowed_hosts), with up to 8 public IPs each; re-resolved every 5 minutes, and a DNS failure keeps the last known value |
| Open to everyone | Only with allow_any and confirm_open together; the answer says so in open_to_any |
The list belongs to the engine, not to each database: changing it from any MySQL database changes access for every database of that engine on that VPS (the answer says so with shared_with_engine and affected_apps).
- Missing firewall rules are added, one per CIDR (
ufw allow from <cidr> to any port <port> proto tcp). - Then the extra ones are removed and their open connections are cut.
- Only then is the listener created and verified: rules equal to the list, no rule open to anyone, listener present and a TLS handshake with the right name.
Uninstalling, persistence and backups
- Without
delete_data(the default): the app's own external access is closed and it stops being listed as installed, but the database, its user and its data stay on the VPS. The next reconciliation shows it as discovered. Nothing is lost. - With
delete_data: true(confirmed twice, admins only): the database is dropped and, if Apps created the account, the account and its stored password too. Never a customer account norroot. - The engine is never uninstalled from Apps: it stays installed even with no databases.
- If the VPS or the engine do not answer,
desinstalar_appwithforzar=truetries each step once, removes the app and lists innot_cleanedwhat was left on the server. - Data lives on the VPS disk and travels in its weekly backup, which is taken while the VPS is running. For a consistent copy of a specific database it is better to also make your own dump from the database manager (export) or with the engine's tools.
Diagram
- App on the same VPS:
127.0.0.1:3306 - External client:
db-<id>:<port>with TLS identity verification - Host firewall and proxy: listed IPs only → VPS 3306
- mysqld: terminates TLS; the account requires SSL and sees only its database
Expected result
The URI (without password) appears once external access is ready; the full one is obtained with revelar_credencial_app.
mysql://app<id>_<hex>@db-<vps>.<zona>:<puerto>/<base>?ssl-mode=VERIFY_IDENTITY
mysql://app<id>_<hex>@db-<vps>.<zona>:<puerto>/<base>?sslMode=verify-full (MariaDB)Errors and troubleshooting
| Error | Cause | Retry? |
|---|---|---|
motor_no_soportado | Not MySQL 8.x nor MariaDB 10.4+; nothing is touched | No |
base_ya_existe | A database with that name exists (case-insensitive) on which the account has no grants | No |
motor_detenido / base_no_creada / permisos_no_verificados | Engine stopped, failed SQL or grants that did not verify | Yes |
requiere_reinicio | MariaDB needs a restart for the first TLS; access does not open until resolved | After restarting |
| External access not requested | The engine has anonymous or passwordless accounts the listener would expose | After removing those accounts |
The other causes (IP list, TLS, old credentials, unverified closing) are the same as in PostgreSQL. Follow-up is with ver_operacion_app and reintentar_operacion_app.
MCP access
The whole database lifecycle can be operated over MCP with the permissions of the person who authorized the agent. Reference at /mcp/docs and /mcp/bases-de-datos.
- Read:
ver_catalogo_apps,ver_preflight_app,listar_apps_empresa,ver_app,ver_acceso_app(host, port, TLS and IP list, no secrets),ver_operacion_app. - Modify:
instalar_appwithapp_type: mysql(requiresidempotency_key),cambiar_lista_ip_app(only if its owner is an admin; IPs must be named explicitly: the agent's IP is not the person's),reintentar_operacion_app,refrescar_apps. - Sensitive:
revelar_credencial_app(admins only, noidempotency_key; the answer is markedsensible: trueand the audit records that it was revealed, never the value). - Destructive:
revocar_acceso_app(closes external access for the whole engine) anddesinstalar_app(withdelete_datait erases data; requiresconfirm_delete_data).
To read and write data inside the database, the agent uses the database manager tools. An agent authorized as read-only does not see the tools that modify and cannot reveal credentials.