GuiderUtveckling och driftMySQL-databasserver

Kör en MySQL-databasserver med Podman utan root

MySQL 9.7 LTS på Debian 13 som din egen databasserver, i rootless Podman under en egen användare, med en databas och ett konto per app, TLS, inställningar för 8 GB, åtkomst bara via WireGuard, och nattliga säkerhetskopior du har återställt.

Testad på MySQL Community Server 9.7.2 på Debian 13 (trixie) på en server hos Melonslab Uppdaterad 14 oktober 2026

Rekommenderad server för guiden

SSD RAID 1 · ECC DDR4 · 10 Gbit

VC-P Alloy · 2 vCPU · 8 GB Minne · 60 GB Lagring

Månadsvis, ingen bindningstid 7 dagars öppet köp

100 kr/mån

Beställ nu
På den här sidan

Det här sätter du upp

En hanterad databastjänst ger dig en adress, ett användarnamn och ett lösenord, och sköter resten. Den här guiden bygger samma sak på din egen server: MySQL med en databas och ett konto per app, inställningar anpassade efter servern, TLS, nattliga säkerhetskopior, och åtkomst bara från servern själv och från de servrar där dina appar körs, via WireGuard. Databasportarna är aldrig öppna mot internet.

MySQL utvecklas av Oracle Corporation i USA, och MySQL är Oracles varumärke. MySQL Community Server, som används här, är öppen källkod under GPL version 2; Oracle säljer också en Enterprise Edition med fler verktyg och support. Containeravbilden är den officiella mysql på Docker Hub, som Dockers projekt Official Images bygger från Oracles paket. På vår testserver öppnade MySQL inga utgående anslutningar.

Här körs MySQL som en enda container under en egen användare som heter mysql, uppsatt som i Podman-guiden. Den behöver ingen Caddy: apparna pratar direkt med den, genom tunneln.

Varje steg nedan har körts på en nyinstallerad Melonslab VC-P Alloy (2 vCPU, 8 GB) med Debian 13:

  • Svensk text som Åsa Öberg-Ärlig, inskickad via en pipe till klienten mysql i containern, sparades som UTF-8.
  • MySQL 9.7.2, den aktuella versionen med långtidsstöd (LTS), startade med ett root-lösenord från en hemlighet i Podman, och root kunde bara logga in inifrån containern.
  • Ett konto för en app, från en appserver i andra änden av en WireGuard-tunnel, kunde skapa tabeller i sin egen databas, men inte öppna en annan databas, skapa databaser eller läsa filer på servern. Utan TLS nekades det.
  • Klienten mysql från Debian och en app i Python anslöt med TLS 1.3 och kontrollerade serverns certifikat. En klient med fel certifikatutfärdare nekades.
  • Ingenting lyssnade på serverns publika IPv4- eller IPv6-adress, och från internet svarade portarna 3306 och 33060 inte.
  • En körning med sysbench före och efter minnesinställningarna.
  • En nattlig dump återställdes bredvid den riktiga databasen, och i en tom server med sina konton. En fysisk säkerhetskopia med Percona XtraBackup återställdes över den data som var i drift.
  • En uppgradering av huvudversion från MySQL 8.4 till 9.7 genom att byta taggen på avbilden.
  • Allt startade igen av sig självt efter en omstart av servern, även tunneladressen.

Efter en omstart använde hela servern 1,2 GB minne, varav MySQL-containern 830 MB. MySQL:s cache tar mer allteftersom den fylls med din data, upp till de 4 GB som ställs in i steg 4.

Innan du börjar

Du behöver:

Är appservern en annan server hos Melonslab i samma nät behöver du först Anslut två servrar, så att WireGuard når den. Tunneln ger sedan kryptering och en fast adress för varje server.

Exemplen använder appdb och appuser för appens databas och konto, och 203.0.113.10 för databasserverns publika adress. Byt ut dem mot dina egna genomgående. MySQL använder port 3306, liksom MariaDB, så kör en av dem per server.

1. Skapa användaren

Som root:

useradd -m -s /bin/bash mysql
loginctl enable-linger mysql

MySQL ska lyssna på tunneladressen, 10.8.0.1. Vid uppstart kan användarens tjänster starta innan WireGuard har skapat den adressen, och då stannar MySQL med Can't start server: Bind on TCP/IP port: Cannot assign requested address. Den här inställningen låter program lyssna på en adress som servern inte har än, så att MySQL startar oavsett vilken som kommer först:

echo "net.ipv4.ip_nonlocal_bind = 1" > /etc/sysctl.d/60-nonlocal-bind.conf
sysctl --system

Byt sedan till den nya användaren:

machinectl shell mysql@

Allt fram till steg 7 körs som mysql.

2. Skapa hemligheterna

openssl rand -base64 24 | tr -d '\n' | podman secret create mysql-root-password -
pw=$(podman secret inspect --showsecret --format '{{.SecretData}}' mysql-root-password)
printf '[client]\nuser=root\npassword="%s"\ndefault-character-set=utf8mb4\n' "$pw" | podman secret create mysql-root-client -
openssl rand -base64 24 | tr -d '\n' | podman secret create appdb-password -

Den första är MySQL:s root-lösenord. Den andra innehåller samma lösenord som en inställningsfil för klienten, som kommandot mysql i containern läser, så att du kan logga in som root utan att skriva det. default-character-set=utf8mb4 gör att klienten skickar text som UTF-8: utan den använder den latin1 när du skickar in SQL via en pipe, och ett namn som Åsa sparas som Ã…sa. Den tredje är lösenordet till appens konto. Podman lagrar dem, så de står inte i någon av filerna nedan. Så här visar du ett, till din lösenordshanterare eller appens inställningar:

podman secret inspect --showsecret --format '{{.SecretData}}' appdb-password

3. Skapa ett TLS-certifikat

WireGuard krypterar redan trafiken mellan dina servrar. TLS lägger till en kontroll av att appen pratar med din databas och inte med något annat i tunneln, och håller anslutningen krypterad om den någon gång tar en annan väg. Eftersom ingen utanför dina servrar ansluter behöver du inget publikt certifikat: skapa en egen liten certifikatutfärdare, och ett certifikat för servern som den signerar.

mkdir -p ~/mysql/ca ~/mysql/tls ~/mysql/conf.d
cd ~/mysql/ca
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -days 3650 \
  -subj "/CN=MySQL CA" -addext "basicConstraints=critical,CA:TRUE" \
  -addext "keyUsage=critical,keyCertSign,cRLSign" -keyout ca-key.pem -out ca.pem
openssl req -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -subj "/CN=10.8.0.1" \
  -addext "subjectAltName=IP:10.8.0.1,IP:127.0.0.1" -keyout ../tls/server-key.pem -out server.csr
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca-key.pem -days 3650 -copy_extensions copy -out ../tls/server-cert.pem
cp ca.pem ../tls/
podman unshare chown 999:999 ../tls/server-key.pem
  • ca.pem är certifikatutfärdaren. Varje appserver får en kopia, som den kontrollerar servern med.
  • ca-key.pem signerar certifikat. Den stannar i ~/mysql/ca, som containern aldrig ser.
  • Serverns certifikat anger adresserna som apparna ansluter till, 10.8.0.1 och 127.0.0.1. Klienterna jämför det med adressen de använde.
  • Båda gäller i tio år. Lägg in en påminnelse i kalendern och kör de fyra sista kommandona igen innan dess.
  • Sista raden ger serverns nyckel till användaren som MySQL körs som i containern, användar-ID 999, så att MySQL kan läsa den men inte andra användare på servern.

Utan de här filerna skapar MySQL ett eget självsignerat certifikat när den startar, men då har klienterna inget att kontrollera det mot.

4. Anpassa den efter servern

MySQL:s standardinställningar är gjorda för att starta på vilken maskin som helst, med 128 MB till sin egen cache. Skapa ~/mysql/conf.d/server.cnf:

[mysqld]
# Listen on this server and on the WireGuard tunnel only
bind_address = 127.0.0.1,10.8.0.1
mysqlx = OFF

# TLS for connections over the network
ssl_ca = /etc/mysql/tls/ca.pem
ssl_cert = /etc/mysql/tls/server-cert.pem
ssl_key = /etc/mysql/tls/server-key.pem

# Sized for 2 vCPU and 8 GB of memory
innodb_buffer_pool_size = 4G
innodb_redo_log_capacity = 1G
max_connections = 200
tmp_table_size = 64M
max_heap_table_size = 64M
binlog_expire_logs_seconds = 604800
  • bind_address får MySQL att lyssna på servern själv och i tunneln, och ingen annanstans. Även med brandväggen avstängd lyssnar ingenting på den publika adressen.
  • mysqlx = OFF stänger av X Protocol, ett andra gränssnitt för MySQL Shell och appar som använder MySQL som dokumentdatabas. Som standard lyssnar det på port 33060 på alla adresser. Vanliga appar och klienter använder det inte.
  • innodb_buffer_pool_size är MySQL:s cache för tabeller och index. MySQL läser förbi systemets filcache, så på en server som bara kör databasen är hälften till två tredjedelar av minnet den vanliga storleken. 4 GB lämnar plats åt systemet, anslutningarna och några andra containrar.
  • innodb_redo_log_capacity är redo-loggen, dit ändringar skrivs innan de hamnar i tabellerna. En större logg än standardvärdets 100 MB jämnar ut tung skrivning.
  • max_connections höjs från standardvärdet 151. Varje anslutning använder lite eget minne, högst några MB för vanliga frågor.
  • tmp_table_size och max_heap_table_size låter en fråga hålla större tillfälliga tabeller i minnet innan de flyttas till disk.
  • MySQL för en binärlogg över varje ändring, som replikering och återställning till en viss tidpunkt använder. Som standard sparas 30 dagar av den, vilket kan ta mycket disk på en server med mycket trafik. binlog_expire_logs_seconds sparar sju dagar. Räkna ändå med diskutrymme för den: i våra tester med sysbench växte sju dagars binärlogg till 2,5 GB. podman exec mysql mysql -e "SHOW BINARY LOGS" visar filerna och deras storlek.

På paketet Titanium, med 16 GB, använder du innodb_buffer_pool_size = 10G.

5. Kör MySQL

mkdir -p ~/.config/containers/systemd

Skapa ~/.config/containers/systemd/mysql.container:

[Unit]
Description=MySQL database server

[Container]
ContainerName=mysql
Image=docker.io/library/mysql:9.7
Network=host
Volume=mysql-data:/var/lib/mysql
Volume=mysql-run:/var/run/mysqld
Volume=%h/mysql/conf.d:/etc/mysql/conf.d:ro
Volume=%h/mysql/tls:/etc/mysql/tls:ro
Secret=mysql-root-password
Secret=mysql-root-client,target=/root/.my.cnf,mode=0400
Environment=MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql-root-password MYSQL_ROOT_HOST=localhost
HealthCmd=mysqladmin ping -h 127.0.0.1 --silent
HealthInterval=10s
Notify=healthy
AutoUpdate=registry

[Service]
Restart=always
TimeoutStartSec=300

[Install]
WantedBy=default.target

Vad raderna gör:

  • mysql:9.7 är den aktuella LTS-versionen, med Oracles fullständiga support till april 2031. AutoUpdate=registry installerar dess bugg- och säkerhetsfixar, som 9.7.2 till 9.7.3, men flyttar dig aldrig till en ny huvudversion. Taggarna mysql:latest och innovation följer i stället innovationsversionerna, som får nya funktioner varje kvartal och bara stöds fram till nästa.
  • Network=host sätter MySQL på serverns eget nätverk, som Caddy. Då lyssnar den på exakt adresserna från steg 4, och ser varje klients riktiga adress, som kontona i steg 6 bygger på.
  • Root-lösenordet kommer från hemligheten. MYSQL_ROOT_HOST=localhost skapar root bara för anslutningar inifrån containern. Utan den skapar avbilden också ett root-konto som får logga in från vilken adress som helst.
  • mysql-root-client blir /root/.my.cnf i containern, så att podman exec mysql mysql loggar in dig som root.
  • Volymen mysql-run innehåller MySQL:s socket, som den fysiska säkerhetskopian i steg 10 ansluter genom.
  • Notify=healthy gör att tjänsten räknas som startad först när MySQL svarar, så att allt som väntar på den väntar tillräckligt länge.

Starta den, och slå på automatiska uppdateringar:

systemctl --user daemon-reload
systemctl --user start mysql
systemctl --user enable --now podman-auto-update.timer
podman exec mysql mysql -e "SELECT VERSION(), @@innodb_buffer_pool_size, @@bind_address"
VERSION()   @@innodb_buffer_pool_size   @@bind_address
9.7.2   4294967296  127.0.0.1,10.8.0.1

Vid första starten laddas avbilden på 952 MB ner, och det tar ungefär en minut.

6. Skapa en databas och ett konto för varje app

Ge varje app en egen databas och ett eget konto, så att en läcka i en app inte öppnar de andra. Skapa dem som root i MySQL, med lösenordet från steg 2:

pw=$(podman secret inspect --showsecret --format '{{.SecretData}}' appdb-password)
podman exec -i mysql mysql <<SQL
CREATE DATABASE appdb;
CREATE USER 'appuser'@'10.8.0.2' IDENTIFIED BY '$pw' REQUIRE SSL;
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, INDEX, REFERENCES
  ON appdb.* TO 'appuser'@'10.8.0.2';
SQL
  • 'appuser'@'10.8.0.2' är ett konto som bara kan logga in från appserverns tunneladress. Samma namn från en annan adress nekas.
  • REQUIRE SSL nekar kontot utan TLS.
  • Rättigheterna täcker vad en app och dess migreringar behöver i sin egen databas: läsa och skriva rader, och skapa, ändra och ta bort tabeller och index. Kontot kan inte skapa databaser eller konton, eller läsa filer på servern.

Vi kontrollerade från appservern, inloggade som appuser:

$ mysql ... mysql
ERROR 1044 (42000): Access denied for user 'appuser'@'10.8.0.2' to database 'mysql'
$ mysql ... -e "CREATE DATABASE other"
ERROR 1044 (42000) at line 1: Access denied for user 'appuser'@'10.8.0.2' to database 'other'

LOAD_FILE('/etc/passwd') gav ingenting tillbaka. Upprepa blocket för varje app, med dess egna namn och en egen hemlighet.

Adressen i kontot är den som anslutningen kommer från:

  • en app på en annan server, genom tunneln: dess tunneladress, som 10.8.0.2;
  • en app på den här servern, utanför en container, eller genom en SSH-tunnel (steg 7): 127.0.0.1;
  • en app i en container på den här servern, under en annan användare: 10.8.0.1, eftersom den når MySQL via tunneladressen och kommer därifrån.

7. Släpp in dina appar

Gå tillbaka till root med exit, och öppna port 3306 bara i tunneln:

ufw allow in on wg0 to any port 3306 proto tcp

Kopiera certifikatutfärdaren, /home/mysql/mysql/ca/ca.pem, till varje appserver, till exempel till /etc/ssl/certs/appdb-ca.pem. Den är inte hemlig.

Med klienten mysql. Debian har ingen klient från Oracle; dess kommando mysql kommer från MariaDB och fungerar med MySQL. Kör apt install mariadb-client på appservern, och sedan:

mysql -h 10.8.0.1 -u appuser -p --ssl-ca=/etc/ssl/certs/appdb-ca.pem appdb \
  -e "SELECT CURRENT_USER(); SHOW STATUS LIKE 'Ssl_version'"
CURRENT_USER()
appuser@10.8.0.2
Variable_name   Value
Ssl_version TLSv1.3

Från en app. I de flesta appar är anslutningssträngen mysql://appuser:LÖSENORD@10.8.0.1:3306/appdb, med sökvägen till ca.pem i dess TLS-inställningar. I Python, med PyMySQL (apt install python3-pymysql):

import os
import pymysql

conn = pymysql.connect(
    host="10.8.0.1",
    user="appuser",
    password=os.environ["DB_PASSWORD"],
    database="appdb",
    ssl_ca="/etc/ssl/certs/appdb-ca.pem",
    ssl_verify_cert=True,
    ssl_verify_identity=True,
)
with conn.cursor() as cur:
    cur.execute("SELECT COUNT(*) FROM notes")
    print(cur.fetchone()[0])
conn.close()

ssl_verify_cert och ssl_verify_identity får PyMySQL att kontrollera att certifikatet är signerat av din utfärdare och anger 10.8.0.1. Ange båda: med bara ssl_verify_identity stannar versionen i Debian 13 med Cannot set verify_mode to CERT_NONE when check_hostname is enabled. MySQL:s standardmetod för lösenord, caching_sha2_password, fungerar i PyMySQL över TLS. Vår testapp läste tillbaka 1 000 rader med TLS 1.3, och med en annan utfärdares ca.pem nekades den med certificate verify failed.

Från din egen dator. En SSH-tunnel behöver ingen ändring på servern. På din dator:

ssh -N -L 3307:localhost:3306 root@203.0.113.10

Medan den körs ansluter ett databasverktyg på din dator till 127.0.0.1 port 3307, och MySQL ser anslutningen komma från 127.0.0.1. Därför behövs ett konto för den adressen (steg 6).

8. Kontrollera vad andra ser

På servern:

ss -tlnp | grep mysqld
LISTEN 0      4096        10.8.0.1:3306      0.0.0.0:*    users:(("mysqld",pid=5679,fd=18))
LISTEN 0      4096       127.0.0.1:3306      0.0.0.0:*    users:(("mysqld",pid=5679,fd=15))

Ingenting på den publika adressen, ingenting på IPv6, och ingen port 33060. Från en dator som inte är ansluten till tunneln provar du den publika adressen:

nc -vz -w 5 203.0.113.10 3306
nc: connect to 203.0.113.10 port 3306 (tcp) timed out: Operation now in progress

ufw kastar försöket utan att svara, och på servern visar journalctl -k | grep 'DPT=3306' det som [UFW BLOCK]. Genom tunneln kommer appservern bara in med de konton du har skapat. Våra tester fick:

--skip-ssl            ERROR 1045 (28000): Access denied for user 'appuser'@'10.8.0.2' (using password: YES)
-u root               ERROR 1045 (28000): Access denied for user 'root'@'10.8.0.2' (using password: YES)
another CA            ERROR 2026 (HY000): TLS/SSL error: self-signed certificate in certificate chain

9. Mät

Vi mätte med sysbench på en testdatabas på 1,9 GB, åtta tabeller med en miljon rader, 16 klienter i 60 sekunder, från servern själv. Varje test kördes två gånger:

InställningarLäsning och skrivning (transaktioner/s)Bara läsning (transaktioner/s)
Standard240 och 102471 och 294
Steg 4277 och 279742 och 817

Med standardcachen på 128 MB varierade resultaten mycket mellan körningarna. Med inställningarna från steg 4 blev bara läsning ungefär dubbelt så snabb, och båda testerna gav jämna resultat. För att köra det själv installerar du sysbench på servern, skapar en databas sbtest och ett konto för 127.0.0.1, och kör sysbench oltp_read_write --mysql-host=127.0.0.1 --tables=8 --table-size=1000000 --threads=16 --time=60 med prepare och sedan run. Ta bort databasen och kontot efteråt.

10. Säkerhetskopiera varje natt

mysqldump skriver en konsekvent kopia av en databas medan apparna fortsätter att använda den. Som mysql (machinectl shell mysql@) skapar du ~/.local/bin/mysql-dump-all:

#!/bin/sh
# Dump the accounts and every database, and keep 14 days of dumps
set -e
umask 077
dir=$HOME/backup
day=$(date +%F)
podman exec mysql mysql -N -e "SELECT CONCAT('SHOW CREATE USER ', QUOTE(user), '@', QUOTE(host), '; SHOW GRANTS FOR ', QUOTE(user), '@', QUOTE(host), ';') FROM mysql.user WHERE user NOT IN ('root', 'mysql.infoschema', 'mysql.session', 'mysql.sys')" \
    | podman exec -i mysql mysql -N --init-command="SET print_identified_with_as_hex = ON" | sed 's/$/;/' > "$dir/users-$day.sql"
for db in $(podman exec mysql mysql -N -e "SHOW DATABASES WHERE \`Database\` NOT IN ('information_schema','performance_schema','sys','mysql')"); do
    podman exec mysql mysqldump --single-transaction --routines --events --set-gtid-purged=OFF "$db" | gzip > "$dir/$db-$day.sql.gz"
done
find "$dir" -type f -mtime +13 -delete
chmod 755 ~/.local/bin/mysql-dump-all
mkdir -p ~/backup ~/.config/systemd/user

Filen users innehåller alla konton utom root, med deras rättigheter och lösenordshashar, som databasdumparna inte tar med: det första kommandot mysql skriver en SHOW CREATE USER och en SHOW GRANTS för varje konto, och det andra kör dem. --set-gtid-purged=OFF utelämnar MySQL:s replikeringspositioner, som annars stoppar återställningen med @@GLOBAL.GTID_PURGED cannot be changed. Bara mysql kan läsa filerna. Kör skriptet varje natt med en systemd-tjänst och en timer. Skapa ~/.config/systemd/user/mysql-dump.service:

[Unit]
Description=Dump MySQL databases

[Service]
Type=oneshot
ExecStart=%h/.local/bin/mysql-dump-all

Och ~/.config/systemd/user/mysql-dump.timer:

[Unit]
Description=Dump MySQL databases every night

[Timer]
OnCalendar=*-*-* 02:30
Persistent=true

[Install]
WantedBy=timers.target

Slå på timern och kör säkerhetskopian en gång nu:

systemctl --user daemon-reload
systemctl --user enable --now mysql-dump.timer
systemctl --user start mysql-dump.service
ls -l ~/backup
-rw------- 1 mysql mysql 5608 Oct 14 02:28 appdb-2026-10-14.sql.gz
-rw------- 1 mysql mysql  980 Oct 14 02:28 users-2026-10-14.sql

Testa en återställning. Återställ i en ny databas, bredvid den riktiga:

podman exec mysql mysql -e "CREATE DATABASE appdb_restore"
gunzip -c ~/backup/appdb-2026-10-14.sql.gz | podman exec -i mysql mysql appdb_restore
podman exec mysql mysql -e "SELECT COUNT(*) FROM appdb_restore.notes; DROP DATABASE appdb_restore"

Räkna rader i en av dina egna tabeller. Vår testtabell, notes, kom tillbaka med alla sina 1 000 rader.

För att bygga upp en hel server igen sätter du upp den med steg 1 till 5, läser in filen users först och sedan varje databas:

podman exec -i mysql mysql < ~/backup/users-2026-10-14.sql
podman exec mysql mysql -e "CREATE DATABASE appdb"
gunzip -c ~/backup/appdb-2026-10-14.sql.gz | podman exec -i mysql mysql appdb

Vi gjorde det i en tom MySQL: kontona kom tillbaka med sina adresser och rättigheter, appen loggade in med sitt gamla lösenord, och tabellen hade alla sina rader. Root behåller den nya serverns lösenord. Spara lösenorden från steg 2 i din lösenordshanterare.

Kopiera ~/backup från servern varje natt, till exempel med restic. Dumparna innehåller all din data.

Fysiska säkerhetskopior för stora databaser

En dump återställs genom att varje sats körs igen, vilket tar lång tid för databaser på många GB. Percona XtraBackup, från Percona i USA, öppen källkod under GPL, kopierar själva datafilerna medan MySQL körs. Det har en egen avbild och ansluter genom socketen i volymen mysql-run:

podman run --rm --user 0 --volumes-from mysql --secret mysql-root-client,target=/root/.my.cnf,mode=0400 \
  docker.io/percona/percona-xtrabackup:9.7 xtrabackup --backup --stream=xbstream --target-dir=/tmp \
  2>~/backup/xtrabackup.log > ~/backup/mysql-physical.xb
tail -1 ~/backup/xtrabackup.log

Sista raden i loggen säger completed OK!. För att återställa den måste MySQL stoppas och dess data bytas ut:

systemctl --user stop mysql
podman volume rm mysql-data
podman volume create mysql-data
podman run --rm -i --user 0 -v mysql-data:/var/lib/mysql docker.io/percona/percona-xtrabackup:9.7 sh -c \
  'mkdir /tmp/r && xbstream -x -C /tmp/r && xtrabackup --prepare --target-dir=/tmp/r && xtrabackup --copy-back --target-dir=/tmp/r --datadir=/var/lib/mysql && chown -R 999:999 /var/lib/mysql' \
  < ~/backup/mysql-physical.xb
systemctl --user start mysql

Vi raderade halva testtabellen efter en fysisk säkerhetskopia, återställde den så här, och alla 1 000 rader var tillbaka. Två saker är bra att veta: avbilden är på 1,6 GB, och när vi testade var den senaste XtraBackup för 9.7 en testversion, 9.7.1-rc1, ett steg efter serverns 9.7.2. Det fungerade, men varje version av XtraBackup är gjord för de versioner av MySQL som fanns när den byggdes, så kontrollera säkerhetskopians logg efter varje uppdatering av MySQL.

11. Uppdatera MySQL

Bugg- och säkerhetsfixar, som 9.7.2 till 9.7.3, kommer med podman auto-update en gång om dagen, och MySQL startar om. Så här ser du om någon väntar:

podman auto-update --dry-run

Huvudversioner. En ny LTS-version kommer ungefär vartannat år, och du kan uppgradera direkt från en LTS till nästa. MySQL uppgraderar sin data själv när en ny version startar på den. Ta en säkerhetskopia, byt taggen i mysql.container och starta om:

systemctl --user start mysql-dump.service
sed -i 's|mysql:9.7|mysql:NEW|' ~/.config/containers/systemd/mysql.container
systemctl --user daemon-reload
systemctl --user restart mysql
podman logs mysql 2>&1 | grep -i upgrade

Byt NEW mot nästa LTS-version. Vi testade det från 8.4 till 9.7, med samma steg:

[Server] Data dictionary upgrading from version '80300' to '90200'.
[Server] Data dictionary upgrade from version '80300' to '90200' completed.
[Server] Server upgrade from '80411' to '90702' started.
[Server] Server upgrade from '80411' to '90702' completed.

Det tog 35 sekunder, och appen anslöt igen med sitt gamla konto. Läs versionsinformationen först: varje LTS tar bort sådant som den förra bara varnade för. Att gå tillbaka till den gamla versionen stöds inte: återställ säkerhetskopian i stället.

Felsökning

MySQL startar inte efter en omstart, och journalctl --user -u mysql visar Bind on TCP/IP port: Cannot assign requested address. MySQL startade före WireGuard, utan inställningen från steg 1. sysctl net.ipv4.ip_nonlocal_bind ska skriva ut 1.

Access denied for user 'appuser'@'10.8.0.2'. Lösenordet, adressen eller TLS är fel. Kontrollera att klienten använder TLS, eftersom kontot har REQUIRE SSL, och att det finns ett konto för exakt den adressen: podman exec mysql mysql -e "SELECT user, host FROM mysql.user".

Host '10.8.0.1' is not allowed to connect to this MySQL server. Appen körs i en container på den här servern och kommer från tunneladressen. Skapa dess konto för 10.8.0.1, som i steg 6.

Containern stannar under första starten, med Access denied for user 'root'@'localhost' (using password: YES). Avbildens installationsskript läste ett lösenord innan root hade något, till exempel från en inställning MYSQL_PWD i containern. Ta bort den, ta sedan bort den halvfärdiga datan med podman volume rm mysql-data och starta igen.

certificate verify failed: CA cert does not include key usage extension. Certifikatutfärdaren skapades utan raden keyUsage i steg 3, som Python 3.13 och senare kräver. Skapa den igen med hela steg 3, och kopiera den nya ca.pem till appservrarna.

TLS/SSL error: self-signed certificate in certificate chain. Klienten har en annan ca.pem än den som signerade serverns certifikat. Kopiera ~/mysql/ca/ca.pem från servern igen.

@@GLOBAL.GTID_PURGED cannot be changed när du återställer en dump. Dumpen gjordes utan --set-gtid-purged=OFF. Lägg till det i skriptet i steg 10 och dumpa igen.

Appen får timeout när den ansluter till 10.8.0.1. Kontrollera att WireGuard är uppe på båda servrarna (wg show), och att ufw-regeln från steg 7 finns: ufw status | grep 3306.

Kör det på din egen server

SSD RAID 1 · ECC DDR4 · 10 Gbit

VC-P Alloy

100 kr/mån

vCPU
2
Minne
8 GB
Lagring
60 GB
Trafik
10 TB
Performance
SSD · RAID 1
  • Full root-åtkomst
  • Nativt /64 IPv6
  • RAID-skyddad lagring
  • Malmö, Sverige
  • Månadsvis, ingen bindningstid
  • 7 dagars öppet köp
Alla guider