Build a complete local OpenTibia stack first, then publish only the endpoints your players need
Canary is a free, open-source MMORPG server emulator for the OpenTibia community. Its Docker quickstart combines the Canary runtime, MariaDB, MyAAC, and a dedicated login-server, giving you a practical way to test the full stack without compiling Canary locally. In this guide, we install the stack, review its environment contract, verify the website, login service, and game port, and cover routine Docker operations. Once everything works locally, we configure remote game access with a Localtonet TCP tunnel and discuss the separate HTTP mapping that remote clients may need for login.
๐ What's in this guide
Understand the Canary Docker architecture
Canary is an OpenTibia MMORPG server emulator written in C++20 and Lua. The project includes the server core, datapacks, Lua scripts, database schema, automated tests, build presets, and development tools. Although Canary can be compiled as a native project, its official repository also includes a lightweight Docker quickstart intended for local development, testing, and LAN demonstrations.
The Docker workflow is the most direct installation path for this guide because it starts the supporting services together and avoids a separate local Canary compilation. Docker Compose creates the service network, starts MariaDB, runs Canary from its published runtime image, builds the MyAAC website image, and starts the dedicated OpenTibiaBR login-server.
This separation matters when you configure a client or publish the installation remotely. The website is available locally at http://localhost:8080, while the dedicated client login webservice is at http://localhost:8088/login. The default game protocol port is 7172. These are three different roles, even though they run as one Compose project.
The quickstart intentionally removes MyAAC's login.php. Point compatible clients at the dedicated login-server endpoint instead. Publishing the MyAAC website alone does not provide the documented client login workflow.
| Component or endpoint | Default local location | Purpose | Remote-access consideration |
|---|---|---|---|
| MyAAC website | http://localhost:8080 |
Website and account management | Use a separate HTTP tunnel only if visitors need the site remotely |
| MyAAC administration | http://localhost:8080/admin |
Website administration | Do not expose casually; protect administrative access |
| Client login webservice | http://localhost:8088/login |
Current client login flow | Usually requires its own HTTP tunnel for remote clients |
| Canary login protocol | TCP port 7171 |
Legacy account login workflow | Needed only when the selected client profile uses this legacy protocol |
| Canary game protocol | TCP port 7172 |
Default current game connection | Primary target for the Localtonet TCP tunnel in this guide |
| Canary status protocol | TCP port 7173 |
Configured status service | Do not publish unless your use case specifically requires it |
| MariaDB | Port 3306 inside the Compose network |
Application database | Keep private; it is not required for players to connect |
Canary also defines separate legacy game ports for the 11.00 and 8.60 runtime profiles. The documented defaults are 7174 and 7175. This guide focuses on the current client workflow and the default game port, 7172. If you intentionally deploy a legacy profile, verify that profile locally and map its corresponding port rather than assuming that every client uses 7172.
Prerequisites for the Docker quickstart
Install the stack on a machine that can remain running while players use it. A desktop is adequate for local testing, while a remotely available server needs appropriate capacity, backups, monitoring, and operating-system maintenance. Capacity depends on your datapack, scripts, map, player count, and workload, so this guide does not prescribe an unsupported universal CPU or memory figure.
You need the following:
- Docker installed and running.
- Docker Compose v2, invoked through the
docker composecommand. - Network access to pull the published Canary image and build the MyAAC image.
- A local copy of the official Canary repository.
- Permission to run Docker commands on the host.
- Free host ports for the endpoints enabled by the supplied Compose configuration.
- A compatible game client for full login and gameplay verification.
Confirm that Docker and the Compose plugin are available before continuing:
docker --version
docker compose version
If either command fails, install or repair Docker before attempting to start Canary. On Linux, an error such as Cannot connect to the Docker daemon normally means that the Docker daemon is not running or that the current user cannot access it. Resolve that host-level Docker issue first rather than changing Canary's Compose files.
You can obtain the repository with Git:
git clone https://github.com/opentibiabr/canary.git
cd canary/docker
Alternatively, download an official repository archive, extract it, and open a terminal in its docker directory. The important point is that the startup command must run from the directory containing the quickstart's Compose files and .env.dist.
Canary documents this stack for local development, testing, and LAN demonstrations. Do not publish it with default database passwords, the default MyAAC administrator password, or enabled test accounts. Complete the local installation first, replace the defaults, review the exposed ports, and decide which services actually need remote access.
Install Canary, MariaDB, MyAAC, and login-server
The standard quickstart has two essential actions: create a writable environment file from the distributed template, then start the Compose project with a build. Run the commands from the repository's docker directory.
Create the local environment file
Copy .env.dist to .env. The distributed file is the template, while .env is the working configuration read by Docker Compose.
Start and build the Compose stack
Run the documented Compose command. Docker pulls the required runtime images, builds the components that are built locally, creates the network and volumes, and starts the services in detached mode.
cp .env.dist .env
docker compose up -d --build
In Windows PowerShell, use the PowerShell copy command if the Unix-style cp command is not available:
Copy-Item .env.dist .env
docker compose up -d --build
Do not interpret a successful return from docker compose up as proof that every application is ready. A container can start and then fail during database initialization, configuration loading, or an application health check. Continue to the verification section and inspect the service logs.
Guarded startup scripts
The repository also provides guarded startup scripts. On Windows PowerShell:
.\up.ps1
On Linux or macOS:
sh ./up.sh
These scripts start Compose with orphan removal and perform a constrained cleanup. They remove stopped containers and dangling images associated with the Compose project, then prune Docker build cache older than seven days. They do not remove Docker volumes, so the MariaDB database and Canary runtime data are preserved.
For LAN-oriented automatic configuration, the documented script forms are:
.\up.ps1 -Lan
LAN=true sh ./up.sh
LAN mode is not the same as internet publication. It prepares the environment for clients on another machine in the local network. A Localtonet deployment still needs a suitable public tunnel, and the address advertised to clients must agree with the reachable game endpoint.
Avoid using docker system prune -a --volumes as routine Canary maintenance. That command can remove data and resources belonging to unrelated Docker projects. The guarded scripts deliberately use narrower cleanup behavior and retain volumes.
Configure the Canary environment safely
Open docker/.env after copying the template. The public Docker configuration contract uses variables beginning with CANARY_. The Compose configuration translates those values into the settings needed by MariaDB, MyAAC, login-server, and the Canary runtime.
Do not create new public settings with unrelated MYSQL_, OT_, or raw Lua variable names when the documented Compose contract already provides a corresponding CANARY_ setting. Unsupported variable names may simply be ignored or may fail to configure all dependent services consistently.
Database configuration
The documented database variables include:
CANARY_DB_HOST
CANARY_DB_PORT
CANARY_DB_NAME
CANARY_DB_USER
CANARY_DB_PASSWORD
CANARY_DB_ROOT_PASSWORD
Inside the supplied Compose network, the database host normally remains db and MariaDB listens on port 3306. The quickstart does not require MariaDB to be published to the host. Canary, MyAAC, and login-server can reach the database through Docker's internal service network.
Replace both database passwords before considering remote use. Use distinct, high-entropy values and store them through an appropriate secrets-management process for your environment. Do not put a real .env file into version control or share it in support screenshots.
Server identity and ports
The quickstart documents the following server-related variables:
CANARY_IMAGE
CANARY_SERVER_NAME
CANARY_SERVER_IP
CANARY_SERVER_LOCATION
CANARY_LOGIN_PORT
CANARY_GAME_PORT
CANARY_LEGACY_1100_GAME_PORT
CANARY_LEGACY_860_GAME_PORT
CANARY_STATUS_PORT
CANARY_STATUS_TIMEOUT
The default current-client game port is 7172. The login protocol uses 7171, the status protocol uses 7173, and the two documented legacy game profiles use 7174 and 7175. Keep these configured ports distinct unless you deliberately create and validate a custom Compose port mapping.
CANARY_SERVER_IP is especially important. It is the address that login-server sends to the current client in the world list. It is not the Docker service name. For a same-machine local test, the documented value is 127.0.0.1. A client on another machine cannot use that loopback address to reach your server because 127.0.0.1 always refers to the client machine itself.
For LAN operation, set it to an address the LAN client can reach. For remote access, the advertised address and game port must correspond to the public endpoint assigned to your game tunnel. Because public host and port assignments can vary, create the Localtonet tunnel before finalizing the value advertised to remote clients. Also verify whether your selected Canary client and profile accept the public endpoint format you receive. Do not guess or substitute a private Docker address.
Image pinning
The quickstart uses the published Canary runtime image, and its convenient default can follow a rolling tag. Rolling tags can change as new builds are published. For a reproducible shared environment, select a specific published image tag or digest that you have tested. This reduces the chance that a later pull changes the runtime while you are diagnosing an unrelated configuration problem.
Do not invent an image tag based only on the Canary application release number. Confirm that the exact container tag or digest exists in the project's published package registry before placing it in CANARY_IMAGE.
Test accounts and administrator credentials
The quickstart can create test accounts and characters. Before remote access, set:
CANARY_TEST_ACCOUNTS=false
Also replace the supplied MyAAC administrator password. The exact surrounding variable names and available website settings can change with the quickstart, so edit the current .env.dist template rather than copying an old configuration from another tutorial.
Verify the local installation before creating a tunnel
Remote access adds another network layer. If the local service is unhealthy, a tunnel cannot repair it. Verify the containers, logs, HTTP endpoints, and game connection independently before configuring Localtonet.
Check container state
docker compose ps
Review every listed service. A service that continually restarts needs log investigation even if another part of the stack appears to work. Initial database setup and application startup can take time, so check again after the first initialization completes.
Inspect service logs
Follow the Canary server log:
docker compose logs -f server
Follow the MyAAC log:
docker compose logs -f myaac
Follow the dedicated login service log:
docker compose logs -f login-server
Use Ctrl+C to stop following a log. This stops the log viewer, not the containers. Pay attention to database authentication errors, connection retries, configuration parsing failures, port binding conflicts, and repeated application restarts.
Verify MyAAC
Open the website in a browser on the Docker host:
http://localhost:8080
The documented administration path is:
http://localhost:8080/admin
Confirm that the public website loads before attempting administration. If the website is unavailable, inspect the myaac logs and confirm that port 8080 is not already occupied by another application.
Verify login-server
The documented current-client login endpoint is:
http://localhost:8088/login
You can inspect basic HTTP reachability from a terminal:
curl -i http://localhost:8088/login
This endpoint is an application webservice, not necessarily a conventional human-readable page. Do not judge the entire login workflow solely by how it looks in a browser. Confirm that the service responds, inspect the login-server logs, and then test it with a compatible client.
Verify the game port
On Windows PowerShell, test whether the host accepts a TCP connection on the default game port:
Test-NetConnection 127.0.0.1 -Port 7172
On a Linux or macOS host with Netcat installed, an equivalent basic reachability test is:
nc -vz 127.0.0.1 7172
A successful TCP probe proves only that something is listening. It does not prove that the selected game client, protocol profile, character, scripts, and datapack work together. Complete a local client login and enter the game before moving on.
Use the correct client path
Configure the current client to use http://localhost:8088/login for a same-machine test. The login-server should return the world information, including the address held in CANARY_SERVER_IP and the port held in CANARY_GAME_PORT. The default local values point the client to 127.0.0.1:7172.
A working MyAAC page confirms only the website. A response from port 8088 confirms only basic login-server reachability. A listening port on 7172 confirms only the TCP listener. Verify the complete sequence from client login through world selection and game entry.
Start, stop, update, and preserve the stack
Day-to-day operation should distinguish between stopping containers and deleting persistent data. Run these commands from the same docker directory so Compose selects the intended project.
Start or rebuild
docker compose up -d --build
Use the guarded platform script instead if you want its documented orphan and old-cache cleanup behavior.
Stop the stack without deleting volumes
docker compose down
This removes the running Compose containers and network while preserving named volumes. It is the appropriate documented stop operation when you intend to keep the database and runtime data.
Delete persisted data
docker compose down -v
-v option is destructive
This removes persisted database and server data associated with the Compose project. Use it only when you intentionally want a clean installation and have already preserved anything important. It is not a normal restart command.
Run the guarded scripts without cleanup
On Windows:
.\up.ps1 -SkipCleanup
On Linux or macOS:
SKIP_CLEANUP=true sh ./up.sh
The documented default build-cache retention window is seven days. If you intentionally want another age window, the scripts accept a cleanup age value. For example, the documented 72-hour forms are:
.\up.ps1 -CleanupUntil 72h
CLEANUP_UNTIL=72h sh ./up.sh
Back up before updates
The quickstart preserves database and runtime data in Docker volumes, but persistence is not the same as a backup. Before changing image versions, Compose definitions, datapacks, scripts, or database configuration, create an environment-appropriate backup of the database and important runtime data. The supplied evidence does not establish a single canonical Canary backup command, storage location, or restoration workflow for every current Compose revision, so verify the volumes and database procedures used by the exact checkout you are running.
Keep a private record of your tested image reference, environment values, client profile, published ports, and custom files. Never include passwords, Localtonet device tokens, or other secrets in a public operations document.
Configure remote Canary access with Localtonet
Once the full client workflow works locally, you can use Localtonet to expose the game service without configuring inbound router port forwarding, changing the router firewall, deploying a VPN, or requiring a public IP address. Our client runs on the machine that can reach Canary and establishes an outbound connection to a Localtonet relay server.
The primary mapping in this guide is a TCP tunnel to 127.0.0.1:7172. The Canary Docker documentation describes the published server ports as TCP ports, and 7172 is the default current game protocol port. If you changed CANARY_GAME_PORT, use the verified host-side game port instead of blindly copying the default.
Install and run the Localtonet client
Run our client on the Docker host or on another device that can reach the Docker host's published game port. The selected device must remain connected for the public tunnel to remain available.
Authenticate the correct device
Select the device-specific authentication token belonging to the client that can reach Canary. Treat this token as a secret. Do not paste it into tutorials, screenshots, source control, or support messages.
Select an available relay server
Choose an available relay server or region from the current dashboard. Available server codes and regions can vary, so use the displayed values rather than copying a hardcoded server code from an old guide.
Create a TCP tunnel to the game service
Configure a TCP tunnel with local IP address 127.0.0.1 and local port 7172, provided that this is the host-side port you verified. If our client runs on another LAN device, use the Docker host's reachable LAN address instead of loopback.
Start the tunnel
Creating a tunnel does not start it. Use the Start button, then wait for the selected device and tunnel to show as connected. Record the assigned public host and port without exposing your authentication token.
Test the public endpoint from an external network
Test from a device that is not relying on the server's local loopback path. Configure the Canary login response and client profile so the advertised game destination agrees with the public endpoint assigned to the tunnel, then complete an actual login and game-entry test.
Saving a Localtonet tunnel configuration does not make it available. The selected device must be connected to our platform, and the tunnel itself must be started. Stopping the client, stopping the tunnel, or shutting down the host makes the public endpoint unavailable.
Configure the advertised game destination
The dedicated login-server advertises CANARY_SERVER_IP and CANARY_GAME_PORT to the current client. For remote gameplay, these values need to describe an endpoint the remote client can actually reach. A private value such as 127.0.0.1, db, a Docker container address, or a private LAN address will not route an internet client through Localtonet.
Compare the Localtonet public host and port with the address format supported by your chosen Canary client and login-server revision. If the public port differs from Canary's local port, the client-facing game port must reflect the externally reachable port while the tunnel continues forwarding to the verified local port. The available public endpoint format depends on the current tunnel configuration, so this article does not invent a fixed hostname or port.
Publish the login webservice separately when required
A TCP tunnel to 7172 forwards the game protocol. It does not automatically publish http://localhost:8088/login. If remote current clients must contact login-server over HTTP, create a separate Localtonet HTTP tunnel targeting local IP 127.0.0.1 and local port 8088.
HTTP tunnels provide a public HTTPS address and can use a random subdomain, a supported custom subdomain, or a custom domain. All three process types serve the local HTTP content. If you choose a custom domain, check the current Localtonet documentation for its DNS requirements rather than guessing records from an older setup.
Start the HTTP tunnel and test the public URL with the /login path. Then configure the compatible client to use that public login URL. The login response must still advertise the separate public game endpoint, not the local 127.0.0.1:7172 destination.
Decide whether to publish MyAAC
MyAAC on port 8080 is another separate HTTP service. If players need public account or website access, create another HTTP tunnel to 127.0.0.1:8080. Do not confuse this address with login-server. Current clients should continue using the dedicated login service at port 8088.
Publishing the MyAAC site also makes its routes reachable through that tunnel, including the administration path unless the application or another access layer restricts it. Change the administrator credentials first and apply the narrowest practical access controls. If only administrators need MyAAC, keeping it local or limiting access is safer than publishing it for everyone.
| Remote need | Local target | Localtonet mapping | Required? |
|---|---|---|---|
| Current game connection | 127.0.0.1:7172 |
TCP tunnel | Yes for the default current game workflow |
| Current client login | 127.0.0.1:8088 |
HTTP tunnel | Usually needed when remote clients use login-server |
| Public MyAAC website | 127.0.0.1:8080 |
HTTP tunnel | Optional |
| Legacy login protocol | 127.0.0.1:7171 |
TCP tunnel | Only for the applicable legacy workflow |
| MariaDB access | Internal Docker service on 3306 |
No player-facing tunnel | No |
Harden the stack before inviting remote users
A tunnel removes the need for inbound router configuration, but it does not convert a development quickstart into a hardened production platform. Any service made public must be treated as internet-accessible. Secure the application, operating system, containers, credentials, and published routes before distributing an endpoint.
Keep the Localtonet device token secret because it identifies the client device that runs the tunnel. Do not place it in Canary's repository, the Docker environment file, a client configuration distributed to players, or an image. If you believe a credential or token has been disclosed, replace it through the appropriate product workflow.
Also review host firewall rules for the ports Docker publishes. A Localtonet tunnel does not require you to open an inbound router port, but Docker's host bindings may still make a service reachable from local interfaces depending on the Compose configuration and host platform. Restrict local-network exposure according to your deployment model.
Troubleshoot common Canary and tunnel problems
Docker Compose cannot start
Confirm that Docker is running and that docker compose version succeeds. Run the command from canary/docker, not the repository root. Also confirm that .env exists and was copied from the current .env.dist in the same checkout.
A container exits or restarts repeatedly
Run docker compose ps, then inspect the affected service with docker compose logs. Database authentication errors often indicate inconsistent environment values. A port-binding error means another host process or container already uses the requested port. Do not repeatedly rebuild without reading the first relevant error.
MyAAC works, but the client cannot log in
MyAAC and login-server are separate services. Confirm http://localhost:8088/login, inspect docker compose logs -f login-server, and verify that the client points to login-server rather than a removed MyAAC login.php route.
Login works, but entering the world fails
This frequently indicates that the address or port advertised by login-server is unreachable from the client. For a same-machine test, verify 127.0.0.1:7172. For a LAN client, use a reachable LAN destination. For a remote Localtonet client, use the public game endpoint assigned to the TCP tunnel and make sure the tunnel still targets the verified local game port.
The Localtonet tunnel is configured but unreachable
Verify all three layers in order:
- Canary is listening locally on the configured host port.
- The selected Localtonet device is connected and can reach that local target.
- The tunnel has been started, not merely created.
If our client runs inside a different machine or network namespace, 127.0.0.1 refers to that device or namespace, not automatically to the Docker host. Use an address reachable from the Localtonet client and verify it locally from that same execution environment.
The public login page responds, but gameplay still fails
The HTTP login tunnel and TCP game tunnel are independent. Confirm that the login response advertises the public game host and public game port, not the login URL, MyAAC URL, a private LAN address, or Docker's internal service name.
Changes to .env do not appear
Recreate the affected Compose services with the documented startup command after saving the file:
docker compose up -d --build
Then inspect the logs and retest locally. Avoid deleting volumes as a generic configuration-refresh technique because that also deletes persistent data.
A legacy client cannot connect
Do not assume it uses the current 7172 game port or HTTP login-server flow. Canary documents separate legacy ports, including defaults for 11.00 and 8.60 profiles. Identify the exact runtime profile, verify it locally, and expose only the corresponding login and game endpoints.
Frequently asked questions
Do I need to compile Canary to follow this guide?
No. The official Docker quickstart is designed to run a local test server without compiling Canary locally. It uses the published Canary runtime image and starts MariaDB, MyAAC, and login-server through Docker Compose.
Which Canary port should I send through a Localtonet TCP tunnel?
For the default current-client workflow documented by the quickstart, use the verified host-side game port, normally TCP 7172. If you changed CANARY_GAME_PORT or use a legacy runtime profile, map the port that your tested profile actually uses.
Is one TCP tunnel enough for remote Canary players?
It is enough to forward the game service itself, but the documented current-client flow also uses the HTTP login service at http://localhost:8088/login. Remote clients may therefore need a separate Localtonet HTTP tunnel for port 8088. MyAAC on port 8080 is optional and requires another mapping if you want a public website.
Why can a remote client log in but not enter the game?
The login service may be reachable while advertising an unusable game address. Check CANARY_SERVER_IP, CANARY_GAME_PORT, and the public Localtonet TCP endpoint. The destination sent to the client must be reachable from the client's network and must correspond to the running game tunnel.
Should I expose MariaDB through Localtonet?
No player-facing workflow in this guide requires public database access. MariaDB is private inside the Docker network by default, and Canary, MyAAC, and login-server communicate with it there. Keep it private unless you have a separately designed, authenticated administrative requirement.
Does Localtonet require router port forwarding or a public IP?
No. Our client establishes an outbound connection to a Localtonet relay server, so the workflow does not require inbound router port forwarding, a public IP address, firewall changes for inbound internet traffic, or VPN setup. The selected Localtonet client and tunnel must remain running.
Does stopping Canary delete the MariaDB database?
Running docker compose down preserves the volumes used for persistent data. Running docker compose down -v removes those volumes and is destructive. The guarded startup scripts also avoid deleting volumes during their cleanup process.
Can I publish only MyAAC and use it for client login?
Not with the documented quickstart workflow. MyAAC's login webservice file is intentionally removed from the quickstart image. Current clients should use the dedicated login-server endpoint at port 8088, while MyAAC remains the website and account administration application.
Make your verified Canary server reachable with Localtonet
Start with a locally working Canary installation, secure its credentials, and then create only the TCP and HTTP mappings your selected client workflow requires. With Localtonet, you can provide remote access without opening inbound router ports or requiring a public IP address.
Get Started Free โ