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#

  1. Edit /etc/mysql/mysql.conf.d/mysqld.cnf:
bind-address = 0.0.0.0
  1. Restart MySQL: systemctl restart mysql.
  2. 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=true to 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 fail2ban on host.
  • Prefer profiles over per-container devices for consistency.

This setup mirrors production containerized DB deployments, balancing isolation and accessibility.