Looking for a new opportunity as DevOps engineer
I am currently seeking a new opportunity as a DevOps Engineer, available from January 2026. I am open to remote or hybrid work from Prague, Czechia (Europe), for a long-term, full-time position or B2B contract. Please feel free to contact me for further details. You can also review my professional background on my LinkedIn profile.
How to Forward MySQL Port 3306 from Ubuntu Host to LXD/LXC Container
Learn step-by-step how to expose your MySQL server running inside an LXD/LXC container on Ubuntu by creating a dedicated profile with a proxy device. This setup forwards traffic from the host’s port 3306 to the container’s MySQL instance, making it accessible externally without direct network bridging. Ideal for secure, isolated database hosting on any Ubuntu system.
Introduction#
LXD (Linux Containers) provides powerful proxy devices to forward host ports to services running inside containers, such as MySQL on port 3306. This approach uses LXD’s built-in proxy functionality, avoiding complex iptables rules or full network exposure. The method leverages profiles for reusability across containers.
The provided commands demonstrate creating a “mysql-3306” profile, adding a proxy device to forward host TCP 3306 to container-local 3306, and applying it to an existing “main” container. This works on Ubuntu hosts (including ARM like Raspberry Pi) with LXD installed, assuming MySQL is already running and bound to 127.0.0.1:3306 inside the container.
Key benefits include:
- Security: Only specified ports are exposed; container remains isolated.
- Reusability: Profiles apply to multiple containers.
- Simplicity: No container restarts needed after profile attachment.
Prerequisites:
- LXD installed and initialized (
sudo snap install lxd; lxd init). - Container running with MySQL server listening on localhost:3306.
- Root or sudo access.
Step-by-Step Guide#
Step 1: Create a Dedicated Profile#
Profiles encapsulate configurations like proxy devices for easy management.
sudo lxc profile create mysql-3306
This creates an empty profile named “mysql-3306”. Verify with sudo lxc profile list.
Step 2: Add Proxy Device to the Profile#
The proxy device listens on the host (0.0.0.0:3306) and forwards to the container’s localhost:3306.
sudo lxc profile device add mysql-3306 hostport3306 proxy listen="tcp:0.0.0.0:3306" connect="tcp:127.0.0.1:3306"
listen: Host binding (all interfaces, port 3306).connect: Container target (localhost:3306).- Device name “hostport3306” is arbitrary but descriptive.
| Option | Description | Example Value |
|---|---|---|
| listen | Host listen address:port | tcp:0.0.0.0:3306 |
| connect | Container connect address:port | tcp:127.0.0.1:3306 |
| type | Device type | proxy |
Inspect the profile:
sudo lxc profile show mysql-3306
Output:
config: {}
description: ""
devices:
hostport3306:
connect: tcp:127.0.0.1:3306
listen: tcp:0.0.0.0:3306
type: proxy
name: mysql-3306
used_by: []
Step 3: Attach Profile to Your Container#
Apply to the target container (e.g., “main” from lxc list).
sudo lxc profile add main mysql-3306
No restart required; proxy activates immediately. Confirm attachment:
sudo lxc config show main --expanded
Look for the profile under profiles.
Step 4: Verify Connectivity#
Test from host:
nc -zv 0.0.0.0 3306
Expected: Connection succeeds. Inside container, MySQL should accept connections.
From external host: Connect to your server’s IP:3306 using MySQL client:
mysql -h <host-ip> -P 3306 -u root -p
Ensure MySQL allows remote binds (bind-address=0.0.0.0 in my.cnf) and user grants (GRANT ALL ON *.* TO 'user'@'%';).
Check container status:
+------+---------+----------------------+------+-----------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+---------+----------------------+------+-----------+-----------+
| main | RUNNING | 192.168.1.243 (eth0) | | CONTAINER | 0 |
+------+---------+----------------------+------+-----------+-----------+
MySQL-Specific Configuration Inside Container#
- Edit
/etc/mysql/mysql.conf.d/mysqld.cnf:
bind-address = 0.0.0.0
- Restart MySQL:
systemctl restart mysql. - Secure remote access:
CREATE USER 'remoteuser'@'%' IDENTIFIED BY 'strongpassword';
GRANT ALL PRIVILEGES ON *.* TO 'remoteuser'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
This prevents binding only to localhost, enabling proxy forwarding.
Troubleshooting Common Issues#
| Issue | Cause | Solution |
|---|---|---|
| Connection refused on host:3306 | Proxy not active or firewall | sudo lxc restart main; Check ufw allow 3306; sudo lxc info main --show-log |
| Proxy fails to bind | Port in use on host | sudo netstat -tlnp | grep 3306; Kill conflicting process or change listen port. |
| Container MySQL unreachable | Bind-address=127.0.0.1 | Set to 0.0.0.0 and restart |
| No external access | NAT/firewall | Ensure host firewall allows 3306; For bridged networks, verify subnet routing. |
| Profile not applying | Syntax error | sudo lxc profile edit mysql-3306 for YAML validation |
Logs: sudo journalctl -u snap.lxd.daemon.
Advanced Options#
- NAT Mode (preserves client IP): Add
nat=trueto proxy (requires static container IP).
sudo lxc profile device set mysql-3306 hostport3306 nat=true
- Multiple Ports: Add more devices, e.g., for 33060.
- UDP Support:
listen=udp:0.0.0.0:3306 connect=udp:127.0.0.1:3306. - Remove Profile:
sudo lxc profile remove main mysql-3306. - Delete Profile:
sudo lxc profile delete mysql-3306.
Security Considerations#
- Exposing MySQL publicly risks attacks; use firewalls (ufw), VPNs, or SSH tunnels.
- Limit user privileges to specific hosts/IPs.
- Monitor with
fail2banon host. - Prefer profiles over per-container devices for consistency.
This setup mirrors production containerized DB deployments, balancing isolation and accessibility.
Looking for a new opportunity as DevOps engineer
I am currently seeking a new opportunity as a DevOps Engineer, available from January 2026. I am open to remote or hybrid work from Prague, Czechia (Europe), for a long-term, full-time position or B2B contract. Please feel free to contact me for further details. You can also review my professional background on my LinkedIn profile.