AzerothCore 3.3.5a

AzerothCore 3.3.5a installs on a clean Debian 13 as root with one long command and runs under pm2. No sudo, no separate user, no Docker.

Updated Oct 28, 2026#wow #debian Based on the AzerothCore community guide for Debian 13
ComponentWhat it is
authserverlogin, port 3724
worldservergame world, port 8085
MySQLdatabases acore_auth, acore_characters, acore_world; user acore
pm2starts both servers and brings them back after a reboot
Client datadbc, maps, vmaps, mmaps from the prebuilt wowgaming/client-data v20.0 package
mod-anticheatoptional anti-cheat module

Requirements#

The heavy part is the build: it needs about 1.5 GB of RAM per compile job. A running server is fine with 2–3 GB.

OS
Debian 13, as root
RAM for the build *
4 GB comfortable
2 GB with swap and one job
Build time *
15–30 min on 4 cores
1–2 h on 1–2 cores
Running *
2–3 GB RAM, 2 vCPU
for 20–30 players
Disk *
40 GB comfortable
25 GB is tight
Open ports (TCP)
3724, 8085
login, game
Game client
3.3.5a
on the player's PC

* Estimates, they depend on the machine. Check the real numbers with the commands in Maintenance. The playerbots module needs far more (hundreds of bots want 8+ GB RAM) and is not used here.

1

Preparation#

Before the long command you need two things: tmux, so a dropped SSH session doesn't kill the build, and the number of compile jobs for your machine.

Install tmux and open a session:

shell
apt update && apt install -y tmux && tmux new -s azeroth

Detach without stopping the build: Ctrl+B, then D. Come back with:

shell
tmux attach -t azeroth

Then, inside tmux, work out J: the number of compile jobs. It takes the smaller of your core count and your RAM at about 1.5 GB per job.

shell · inside tmux
J=$(awk -v c=$(nproc) '/MemTotal/{m=int($2/1024/1024*10/15); if(m<1)m=1; if(m>c)m=c; print m}' /proc/meminfo) && echo "J=$J (cores: $(nproc), RAM: $(free -h | awk '/Mem:/{print $2}'))"

J only lives in this tmux session. With less than 3 GB of RAM, add swap. This is for a regular VM; in an LXC container swap is set by the Proxmox host.

shell
fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile && echo '/swapfile none swap sw 0 0' >> /etc/fstab
2

Install, build and start#

The whole install is one chain joined with &&: if any step fails, the rest don't run. Paste it inside tmux after J is set.

shell · inside tmux · 25 lines
export DEBIAN_FRONTEND=noninteractive && V=0.8.36-1 && apt update && apt install -y git cmake make gcc g++ clang libssl-dev libbz2-dev libreadline-dev libncurses-dev libboost-all-dev lsb-release gnupg wget p7zip-full nodejs npm && npm install pm2 -g &&
mkdir -p ~/mysqlpackages && cd ~/mysqlpackages &&
wget "https://dev.mysql.com/get/mysql-apt-config_${V}_all.deb" &&
wget "https://dev.mysql.com/downloads/gpg/?file=mysql-apt-config_${V}_all.deb&p=37" -O mysql-apt-config_${V}_all.deb.asc &&
gpg --keyserver keyserver.ubuntu.com --recv-keys A8D3785C &&
gpg --verify mysql-apt-config_${V}_all.deb.asc mysql-apt-config_${V}_all.deb &&
dpkg -i ./mysql-apt-config_${V}_all.deb && apt update && apt install -y mysql-server libmysqlclient-dev &&
rm -v ~/mysqlpackages/mysql-apt-config_${V}_all* &&
mysql -e "DROP USER IF EXISTS 'acore'@'localhost'; CREATE USER 'acore'@'localhost' IDENTIFIED BY 'acore' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0; CREATE DATABASE IF NOT EXISTS acore_world DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS acore_characters DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci; CREATE DATABASE IF NOT EXISTS acore_auth DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci; GRANT ALL PRIVILEGES ON acore_world.* TO 'acore'@'localhost' WITH GRANT OPTION; GRANT ALL PRIVILEGES ON acore_characters.* TO 'acore'@'localhost' WITH GRANT OPTION; GRANT ALL PRIVILEGES ON acore_auth.* TO 'acore'@'localhost' WITH GRANT OPTION;" &&
git -C ~/ clone https://github.com/azerothcore/azerothcore-wotlk.git --branch master --single-branch azerothcore &&
git -C ~/azerothcore/modules clone https://github.com/azerothcore/mod-anticheat &&
rm -rf ~/server/data && mkdir -p ~/server/data && cd ~/server/data &&
wget https://github.com/wowgaming/client-data/releases/download/v20.0/data.zip && 7z x data.zip && rm data.zip &&
mkdir -p ~/azerothcore/build && cd ~/azerothcore/build &&
cmake ../ -DCMAKE_INSTALL_PREFIX=$HOME/server/ -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DWITH_WARNINGS=1 -DTOOLS_BUILD=db-only -DSCRIPTS=static -DMODULES=static &&
make -j $J install &&
cp -n ~/server/etc/authserver.conf.dist ~/server/etc/authserver.conf &&
cp -n ~/server/etc/worldserver.conf.dist ~/server/etc/worldserver.conf &&
cp -n ~/server/etc/modules/Anticheat.conf.dist ~/server/etc/modules/Anticheat.conf &&
sed -i -E "s|^DataDir = .*|DataDir = \"$HOME/server/data\"|" ~/server/etc/worldserver.conf &&
sed -i -E "s|^LogsDir = .*|LogsDir = \"$HOME/server/logs\"|" ~/server/etc/*.conf &&
mkdir -p ~/server/logs &&
pm2 start $HOME/server/bin/authserver --name authserver -- -c $HOME/server/etc/authserver.conf &&
pm2 start $HOME/server/bin/worldserver --name worldserver -- -c $HOME/server/etc/worldserver.conf &&
pm2 startup && pm2 save

What it does, in order: packages and pm2, MySQL from the MySQL repository, the databases and the acore user, the source code, prebuilt client data, the compile, configs, start and autostart.

  • Don't need the anti-cheat module? Remove the two lines with mod-anticheat and Anticheat.conf.
  • If wget for MySQL returns 404, the version in V is out of date. Take the current one from the mysql-apt-config download page on dev.mysql.com.

If it fails, don't start over from the top. First run rm -rf ~/azerothcore, otherwise git clone says the folder already exists. Then continue from the step that failed. In a new window, set J and V again.

3

Check it's running#

The server is ready when both processes in pm2 show online and the worldserver log has the line World Initialized.

shell
pm2 status && pm2 logs worldserver --lines 30 --nostream

On first start worldserver fills the databases itself. The log shows Applying '...sql' lines for a few minutes. To follow it live:

shell
pm2 logs worldserver

Leave with Ctrl+C; the server keeps running.

Can't set process priority class, error: Permission denied in the log is harmless. The server just couldn't raise its own priority.

Once the AC> prompt appears, you can create an account.

4

Account and GM rights#

Accounts are created in the worldserver console. The login is a plain username, not an email.

Open the console:

shell
pm2 attach $(pm2 id worldserver | tr -d '[][:space:]')

In the console, with your own name and password:

worldserver console
account create USERNAME PASSWORD
account set gmlevel USERNAME 3 -1

The second command gives the account admin rights (level 3) on all realms. Ctrl+C only detaches you; the server keeps running under pm2.

Check that the account exists and has its rights:

shell
mysql -e "SELECT a.id, a.username, aa.gmlevel, aa.RealmID FROM acore_auth.account a LEFT JOIN acore_auth.account_access aa ON aa.id = a.id;"

The username is stored in capitals, gmlevel should be 3 and RealmID should be -1.

5

Realm address, ports, client#

Out of the box the database holds a local realm address. Players from the internet can log in but can't enter the world until you change it.

Write the address to the database. Replace x.x.x.x with the external IP or domain that clients connect to:

shell
mysql -e "UPDATE acore_auth.realmlist SET address='x.x.x.x' WHERE id=1;"

Check it:

shell
mysql -e "SELECT id,name,address,port FROM acore_auth.realmlist;"

authserver rereads the realm list about every 10 seconds. If it didn't pick up the change, restart it:

shell
pm2 restart authserver

Ports#

Port (TCP)ServicePurpose
3724authserverlogin
8085worldservergame

Forward these two ports on your router to the server. Never open 3306 (MySQL), 7878 or 3443 to the outside.

Client#

Players need a 3.3.5a client. In realmlist.wtf (in Data/<locale>, for example Data/enUS) put one line:

realmlist.wtf
set realmlist x.x.x.x

From your home network the external address may not work if the router has no NAT loopback. In that case put the server's local IP in your own realmlist.wtf.

Maintenance#

Day-to-day control goes through pm2. Updates and backups come down to a few commands.

TaskCommand
Statuspm2 status
World logspm2 logs worldserver
Load per processpm2 monit (leave with q or Ctrl+C)
Restart the worldpm2 restart worldserver
Stop everythingpm2 stop all
Start againpm2 start all
World consolepm2 attach $(pm2 id worldserver | tr -d '[][:space:]') (leave with Ctrl+C)

Resource usage#

Real memory and CPU per process (the RSS column is in kilobytes). Best checked 10–15 minutes after start:

shell
ps -eo pid,comm,rss,pcpu --sort=-rss | head -n 6 && free -h

Update#

Set J first (the command from step 1), then:

shell
cd ~/azerothcore && git pull && git -C modules/mod-anticheat pull && cd build && cmake ../ -DCMAKE_INSTALL_PREFIX=$HOME/server/ -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DWITH_WARNINGS=1 -DTOOLS_BUILD=db-only -DSCRIPTS=static -DMODULES=static && make -j $J install && pm2 restart authserver worldserver

The world applies new SQL updates itself on restart. The community guide also has a ready-made acoreupdate alias that does the same in one command and warns players first.

Database backup#

Uses the default user and password; change them here if you changed them.

shell
for db in acore_auth acore_characters acore_world; do mysqldump -uacore -pacore $db > ~/$db-$(date +%F).sql; done

Configs#

They live in ~/server/etc/: worldserver.conf and authserver.conf. Edit the line you need, then pm2 restart worldserver. The .conf.dist files stay untouched as a reference.

Change the database password#

The default is acore/acore. This command from the community guide asks for a new password and updates both MySQL and the configs:

shell
while true; do read -s -p "Set a new SQL password: " MYSQL_PASSWORD && echo; read -s -p "Retype SQL password: " MYSQL_PASSWORD_CONFIRM && echo; [ "$MYSQL_PASSWORD" = "$MYSQL_PASSWORD_CONFIRM" ] && break || echo "Passwords did not match."; done; unset MYSQL_PASSWORD_CONFIRM && mysql -e "ALTER USER 'acore'@'localhost' IDENTIFIED BY '${MYSQL_PASSWORD}'; FLUSH PRIVILEGES;" && sed -i -E "s|= \"127.0.0.1;3306;acore;[^;]*;|= \"127.0.0.1;3306;acore;${MYSQL_PASSWORD};|" ~/server/etc/*.conf && unset MYSQL_PASSWORD && pm2 restart authserver worldserver

Security#

For a server that's reachable from the internet, what matters most is the database password, closed ports and SSH keys.

  • Database password. The default is acore/acore. MySQL only listens on localhost, so it's tolerable to start with, but change it (see Maintenance).
  • Ports. Only 3724 and 8085 face the internet. Don't forward 3306 (MySQL), 7878 or 3443.
  • Root. The processes run as root, which is fine for an isolated VM. Nothing blocks it, but forums advise against it: if the server is open to everyone, a separate user is safer.
  • Firewall on a VPS. On a VPS facing the internet directly (not behind a home router), turn on the firewall. SSH is allowed first so you don't lock yourself out.
shell · VPS only
apt install -y ufw fail2ban && ufw default deny incoming && ufw default allow outgoing && ufw allow 22/tcp && ufw allow 3724/tcp && ufw allow 8085/tcp && ufw --force enable
  • SSH key instead of a password. Debian forbids root password login over SSH by default, so you need a key either way. Creating one is covered in the WoW Forever guide.
  • Admin rights. Only your own account should have gmlevel 3.

Troubleshooting#

Most install failures come down to running out of memory, leftovers from a previous attempt, or the realm address.

SymptomCauseFix
Killed or internal compiler error in the build logOut of memoryLower J and finish the build: cd ~/azerothcore/build && make -j 1 install, then run the rest of the chain from the cp -n … authserver.conf.dist step
git clone says the azerothcore folder existsLeftover from a failed attemptrm -rf ~/azerothcore and carry on
wget for MySQL returns 404V is out of dateTake the current mysql-apt-config version from dev.mysql.com
A package is missing on apt installapt update warned about a repositoryRead the apt update output and fix the repository
Login works, but you can't enter the worldWrong address in realmlistUpdate address (step 5), restart authserver
Realm shows as offlinePort 8085 not open, or the server isn't runningpm2 status, check forwarding of 8085
Players can't log in at allPort 3724 not openCheck forwarding of 3724
World keeps restarting, the log cuts offworldserver crashespm2 stop worldserver, then pm2 start worldserver --no-autorestart and read the full log
Can't connect from home via the external addressRouter has no NAT loopbackPut the server's local IP in realmlist.wtf
Port 8085 conflictAnother game server on the same hostChange the world port in worldserver.conf and in realmlist

pm2 logs are in ~/.pm2/logs, the server's own logs in ~/server/logs:

shell
tail -n 50 ~/server/logs/Server.log

If there's no Server.log, ls ~/server/logs shows what's there.