AC Sync
Sim racing cockpit

Sync Server Wiki

Install Sync Server on Linux: configure instances, DNS, Nginx and SSL.

A step-by-step guide for Linux setup, config.yml, reverse proxy, HTTPS and content structure.

Quick overview

This guide sets up an AC Sync Server on Linux. At the end, one Sync Server process runs as a systemd service, Nginx handles the reverse proxy, Certbot provides HTTPS, and each instance has its own subdomain and content structure.

  • Target system: Linux server with shell access
  • Important files: syncserver, config.yml, Nginx site and systemd service
  • Ports: typically 5055 internally, public 80 and 443 through Nginx
  • Content: per instance under /syncserver/instances/<host>/sync_root

1. Setup goal

In the end, your sync server should run on a Linux server, be accessible via subdomains and be secured via HTTPS.

The program itself runs as a binary. However, it can serve several instances at the same time. Which instance is used for a request is decided by the hostname of the request.

What are multi-instances?

Multi-instances mean: one Sync Server process serves several logically separated areas at the same time. Each area has its own subdomain and its own data folders.

Example: alpha-sync.example.com and beta-sync.example.com run in the same service, but with separate content under /syncserver/instances/<host>/....

What are they for?

  • Separation of series, leagues or environments (for example live, test, event)
  • Clean data structure per host without mixing

When do you need them?

  • One instance is enough if you only operate one subdomain (one or many series can be in it)
  • You need multiple instances when content/users should remain separated by subdomain
If you run the server behind Nginx, the usual variant is: Sync Server runs locally on 127.0.0.1:5055, Nginx accepts HTTPS on port 443 and forwards it to the local Sync Server.

2. Requirements

  • A Linux server or VPS
  • Root access or sudo
  • Eine Domain, zum Beispiel example.com
  • Ein DNS-Provider, bei dem du A-Records setzen kannst
  • Das Sync Server-Binary
  • Die Configuration file config.yml
  • Nginx and Certbot for productive operation
The folder logs under each instance currently contains only client state files such as clients.json. No classic server logs are written there.
Structure of this manual: Starting with the practical sections, the steps are each Single instance and multiple instances beschrieben.

3. DNS and subdomains

For the domain to be reachable, the subdomains must point to the public IP of your server.

Adjust subdomains and IP live

Enter your subdomains and an example IP here. All examples in the guide are updated instantly without a reload and saved in the browser.

Storage: localStorage in the current browser.

Single instance (one subdomain)

  • Set an A-Record, zum Beispiel alpha-sync.example.com -> 203.0.113.10.
  • In config.yml the same host must be listed in instances[].hosts stand.

Multiple instances (mehrere Subdomains)

Typisches Example:

  • Setze pro Instanz/Subdomain einen eigenen A-Record.
  • alpha-sync.example.com - A auf 203.0.113.10
  • beta-sync.example.com - A auf 203.0.113.10
  • cup-sync.example.net - A auf 203.0.113.10
The DNS records must be active before Certbot can create the SSL certificate. For the HTTP-01 check, the domain must already point to your server.

4. Create base paths

Create a fixed working folder on the server. For you, this is for example /syncserver.

sudo mkdir -p /syncserver/bin

At first you only need the binary path and the location of the configuration file. The instance subfolders under /syncserver/instances legt der Sync Server später selbst an (pro Instanz aus dem ersten Hostnamen abgeleitet).

/syncserver
|-- bin
|   |-- syncserver
|-- config.yml

Important: The file must be exactly under /syncserver/config.yml.

5. Upload binary and config.yml

The file syncserver ist die eigentliche Anwendung, also das ausführbare Programm des Sync Servers. Du musst sie mit einem FTP- or SFTP-Programm deiner Wahl in das Verzeichnis /syncserver/bin hochladen.

The only important thing is that the file ends up exactly here:

/syncserver/bin/syncserver

The configuration file must be here:

/syncserver/config.yml

Only when the binary and config.yml are at these paths, continue with configuration adjustment in the next section.

On startup, the server creates the instance directories under /syncserver/instances automatically from the hosts in the config.yml.

Example:

/syncserver/instances/alpha-sync.example.com/sync_root
/syncserver/instances/alpha-sync.example.com/sync_json
/syncserver/instances/alpha-sync.example.com/logs

6. Adjust config.yml

The configuration determines the server port, instances, and locations. The most important part is the instance model.

Important fields

  • server.host - the IP the binary listens on
  • server.port - the port, for example 5055
  • routing.default_instance - optional fallback to a derived instance ID
  • instances - list of all instances with hosts
  • instances[].id - is automatically derived from instances[].hosts[0] derived (part before the first dot)
  • instances[].hosts - hostnames/subdomains of this instance

Basic principle

When a request with hostname alpha-sync.example.com comes in, the matching instance is used.

The code does this through HTTP host header mapping. Therefore, DNS, nginx and config.yml must match each other.

The directories per instance are automatically formed from the binary location: <base>/instances/<erster-host>/sync_root, <base>/instances/<erster-host>/sync_json and <base>/instances/<erster-host>/logs.

Path logic:
If the binary is under /syncserver/bin/syncserver the base is /syncserver. This automatically creates the instance paths under /syncserver/instances/<erster-host>/....
Important: In config.yml you set only per instance hosts. The id is always derived by Sync Server from the first host part before the first dot. A paths block no longer exists there.

How the config is roughly structured

config.yml |-- server |-- routing |-- instances |-- <instanz> |-- hosts
Folder / section File / value Configuration file

7. Set up a single instance

If you operate only one domain, one instance is enough. This is the simplest entry point.

Even with a single instance, multiple series folders under sync_root .
  1. Define hosts for your instance.
  2. Set server.host to 127.0.0.1, if Nginx sits in front of it.
  3. Set server.port to a free internal port, for example 5055.
  4. Enter exactly one instance with your subdomain in instances.
  5. Optional: Set routing.default_instance to the derived instance ID (for example alpha-sync) or leave it at "" for no fallback.
/syncserver
|-- bin
|   |-- syncserver
|-- config.yml
|-- instances
    |-- alpha-sync.example.com
        |-- sync_root
        |-- sync_json
        |-- logs
Folder File / binary Configuration file
server:
  host: "127.0.0.1"
  port: 5055

routing:
  default_instance: ""

instances:
  - hosts:
      - alpha-sync.example.com

8. Set up multiple instances

Multiple instances are useful when you want to keep areas/environments separated per subdomain. One instance can still contain multiple series. Each instance gets:

  • own hosts/subdomains
  • own series folders (created automatically)
  • own JSON output (created automatically)
  • own client state files in the logs-Ordner

How assignment works

The server looks at the hostname of the request. If the request comes to beta-sync.example.com the instance with this host is used.

If no host matches, routing.default_instance is used. If the value is empty (""), the request is rejected.

Even with multiple instances, you only set hosts. The instance paths are calculated automatically from the first hostname.

Example with multiple instances

/syncserver
|-- bin
|   |-- syncserver
|-- config.yml
|-- instances
    |-- alpha-sync.example.com
    |   |-- sync_root
    |   |-- sync_json
    |   |-- logs
    |-- beta-sync.example.com
    |   |-- sync_root
    |   |-- sync_json
    |   |-- logs
    |-- cup-sync.example.net
        |-- sync_root
        |-- sync_json
        |-- logs
Folder File / binary Configuration file
routing:
  default_instance: ""

instances:
  - hosts:
      - alpha-sync.example.com

  - hosts:
      - beta-sync.example.com

  - hosts:
      - cup-sync.example.net
Important: The first host part before the first dot must be unique per instance (this creates the id). Each subdomain should occur in only one instance.

9. Nginx as reverse proxy

In practice, the sync server usually does not run directly on port 443. Nginx accepts the HTTPS connection and forwards it to the local sync server.

Why Nginx?

  • HTTPS-Termination
  • clean subdomain separation
  • simpler SSL handling
  • the Sync Server can stay internally on 127.0.0.1 remain

Single instance: Nginx template per instance

The following pattern is the unified template for a single instance. For this, take a separate nginx file for each instance, for example /etc/nginx/sites-available/alpha-sync.example.com.conf, and adjust only the domain in server_name and in Hostheader. This template first runs over port 80. HTTPS and the SSL certificate part will be supplemented by Certbot in the next step.

upstream sync_backend {
    server 127.0.0.1:5055;
    keepalive 128;
}

server {
    listen 80;
    server_name alpha-sync.example.com;

    proxy_http_version 1.1;
    proxy_set_header Connection "";

    access_log /var/log/nginx/alpha-sync.access.log;
    error_log  /var/log/nginx/alpha-sync.error.log;

    gzip on;
    gzip_types
        text/plain
        text/css
        application/json
        application/javascript
        text/xml
        application/xml
        application/xml+rss
        text/javascript
        application/octet-stream;
    gzip_min_length 512;
    gzip_comp_level 5;
    gzip_vary on;
    gzip_proxied any;
    gzip_disable "msie6";

    client_max_body_size 1024m;
    proxy_read_timeout 600s;
    proxy_send_timeout 600s;

    location = /health {
        proxy_pass http://sync_backend;
        proxy_set_header Host alpha-sync.example.com;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache off;
        proxy_no_cache 1;
        proxy_cache_bypass 1;
        proxy_buffering off;
        add_header Cache-Control "no-store" always;
    }

    location = /healthz {
        proxy_pass http://sync_backend/health;
        proxy_set_header Host alpha-sync.example.com;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache off;
        proxy_no_cache 1;
        proxy_cache_bypass 1;
        proxy_buffering off;
        add_header Cache-Control "no-store" always;
    }

    location = /list_projects {
        proxy_pass http://sync_backend;
        proxy_set_header Host alpha-sync.example.com;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Accept-Encoding "";
        proxy_cache off;
        proxy_no_cache 1;
        proxy_cache_bypass 1;
        add_header Cache-Control "no-store, must-revalidate, max-age=0" always;
    }

    location ^~ /file_list/ {
        proxy_pass http://sync_backend;
        proxy_set_header Host alpha-sync.example.com;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Accept-Encoding "";
        proxy_cache off;
        proxy_no_cache 1;
        proxy_cache_bypass 1;
        add_header Cache-Control "no-store, must-revalidate, max-age=0" always;
    }

    location ^~ /download_file/ {
        proxy_pass http://sync_backend;
        proxy_set_header Host alpha-sync.example.com;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache off;
        proxy_no_cache 1;
        proxy_cache_bypass 1;
        proxy_request_buffering off;
        proxy_buffering off;
        proxy_max_temp_file_size 0;
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }

    location / {
        proxy_pass http://sync_backend;
        proxy_set_header Host alpha-sync.example.com;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Accept-Encoding "";
        proxy_cache off;
        proxy_no_cache 1;
        proxy_cache_bypass 1;
        proxy_buffering on;
        proxy_buffers 32 64k;
        proxy_busy_buffers_size 256k;
        proxy_max_temp_file_size 1024m;
    }

}

Multiple instances: how to adjust the Nginx configuration

  • insert a separate file for each instance in /etc/nginx/sites-available
  • copy the content of the template completely into this new file
  • change server_name alpha-sync.example.com to the new subdomain
  • change each proxy_set_header Host alpha-sync.example.com to the same new subdomain
  • optionally adjust the log files so each instance gets its own Nginx logs
  • if possible, use your own configuration file per instance so that changes remain cleanly separated

Example of a second instance

If you have additional beta-sync.example.com you want to operate, you create a second file:

/etc/nginx/sites-available/beta-sync.example.com.conf

Copy exactly the same Nginx configuration as above into this file. Then replace only the domain:

  • alpha-sync.example.com becomes beta-sync.example.com
  • <id>.access.log and <id>.error.log as the schema
  • alpha-sync.access.log optionally becomes beta-sync.access.log
  • alpha-sync.error.log optionally becomes beta-sync.error.log
If you use Nginx, leave server.host in Sync Server set to 127.0.0.1. Then the service is not directly reachable from the internet. The port 5055 remains the same for multiple instances if all instances run through the same Sync Server process. The mapping is then done using the hostname from the request.

10. SSL with Certbot

The subdomains should be accessible via HTTPS. This is usually done using Certbot with Nginx.

Install Certbot

sudo apt update
sudo apt install nginx certbot python3-certbot-nginx

Request certificate by scenario

Variant A: One instance with one subdomain

If you only have one instance, request the certificate for just that one domain.

sudo certbot --nginx -d alpha-sync.example.com

Certbot then extends the Nginx file for this subdomain with HTTPS and redirect.

Variant B: Multiple instances with multiple subdomains

With multiple instances, you have two clean options:

  1. one shared certificate with multiple -d entries
  2. one separate certificate per subdomain

Example of a common certificate:

sudo certbot --nginx \
  -d alpha-sync.example.com \
  -d beta-sync.example.com \
  -d cup-sync.example.net

Example per subdomain (run one after the other):

sudo certbot --nginx -d alpha-sync.example.com
sudo certbot --nginx -d beta-sync.example.com
sudo certbot --nginx -d cup-sync.example.net

What matters here

  1. The DNS records must already point to the server.
  2. Nginx must be reachable on port 80.
  3. The domain must be resolvable, otherwise Certbot cannot check.
  4. After successful issuance, Certbot takes over the HTTPS configuration in Nginx.

11. Start as a service

To make the server start automatically after a reboot, it is best to use a systemd service.

Opening file

sudo nano /etc/systemd/system/syncserver.service

Then copy the following service block completely into the editor and save it.

Service-Block

[Unit]
Description=Sync Server
After=network.target

[Service]
WorkingDirectory=/syncserver
ExecStart=/syncserver/bin/syncserver --config /syncserver
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

Execute after

sudo systemctl daemon-reload
sudo systemctl enable --now syncserver
sudo systemctl status syncserver

Single instance vs. multiple instances

  • Single instance: one service is enough.
  • Multiple instances: also a service; the separation takes place via config.yml and hostnames.

If you run multiple instances in the same binary, one service is enough. All instances are loaded from the same config.yml.

12. Test and checks

When everything is set up, check the chain from the inside out:

  1. is the sync server running locally?
  2. does Nginx answer on port 80/443?
  3. do the DNS records point to the server?
  4. is the SSL certificate valid?
  5. is the right host routed to the right instance?
Tests with curl are optional. The core test remains the browser test.

Optional curl tests: single instance

curl -I http://127.0.0.1:5055
curl -I https://alpha-sync.example.com

Optional curl tests: multiple instances

curl -I http://127.0.0.1:5055
curl -I https://alpha-sync.example.com
curl -I https://beta-sync.example.com
curl -I https://cup-sync.example.net
If the domain answers via HTTPS and the correct instance is delivered, the setup is complete.

Common mistakes

  • 404 or wrong data: Hostname in Nginx or config.yml does not match.
  • Certbot fails: DNS does not point to the server yet or port 80 is blocked.
  • Page not reachable: Nginx is not running or the firewall blocks 80/443.
  • Sync Server does not start: Permissions on the folders are missing or the port is already in use.

13. Provide content

Here you define where your actual files are located. Sync Server always reads content from sync_root of the respective instance.

Target path per instance

  • Single instance: /syncserver/instances/alpha-sync.example.com/sync_root
  • Multiple instances: one separate path per host /syncserver/instances/<host>/sync_root
  • Upload content only to sync_root, not to sync_json or logs
  • If you work with Samba: the share must point to exactly this sync_root path

Required structure in the series folder

Every series folder under sync_root must have a content subfolder. Only files in this tree are indexed.

/syncserver
|-- instances
    |-- alpha-sync.example.com
        |-- sync_root
            |-- Formula1
            |   |-- content
            |       |-- cars
            |       |-- tracks
            |-- TouringCup_pw123_
                |-- content
                    |-- cars
Optional password protection per series folder: <name>_<passwort>_
Example: TouringCup_pw123_ is publicly known as TouringCup and requires the password pw123.

Important rules

  • Store only the series that belong to this host in each instance
  • Avoid duplicate series names, even if only capitalization differs
  • Files outside of content are not included in the series JSON
  • sync_json is generated by the server; do not edit these files manually
  • Check file rights after upload: the sync server user must be able to read (typically syncsrv:syncsrv)

Single instance: quick check after upload (curl optional)

ls -la /syncserver/instances/alpha-sync.example.com/sync_root
curl -sS -H "Host: alpha-sync.example.com" http://127.0.0.1:5055/list_projects
curl -sS -H "Host: alpha-sync.example.com" http://127.0.0.1:5055/file_list/Formula1.json | head

Multiple instances: check per host (curl optional)

curl -sS -H "Host: alpha-sync.example.com" http://127.0.0.1:5055/list_projects
curl -sS -H "Host: beta-sync.example.com"  http://127.0.0.1:5055/list_projects
curl -sS -H "Host: cup-sync.example.net"   http://127.0.0.1:5055/list_projects
If only the expected series appear per host, the content is in the correct instance path and host routing is correct.

Recommended overall workflow

Procedure for an instance

  1. Set a DNS-A record for this one subdomain.
  2. Create base paths under /syncserver (at least /syncserver/bin).
  3. Upload syncserver and config.yml to the server.
  4. Adjust config.yml with exactly one instance (hosts).
  5. Configure nginx site for this one subdomain.
  6. Run Certbot for this subdomain.
  7. Start Sync Server as a systemd service.
  8. Load content into /syncserver/instances/<host>/sync_root and check the structure.
  9. Test this subdomain in the browser (curl optional).

Process for multiple instances

  1. Set a DNS-A record for each subdomain.
  2. Create base paths under /syncserver (at least /syncserver/bin).
  3. Upload syncserver and config.yml to the server.
  4. Adjust config.yml with multiple instances (hosts).
  5. Configure a separate nginx site for each subdomain.
  6. Certbot with multiple -d or run individually for each subdomain.
  7. Start Sync Server as one systemd service (one process, multiple instances).
  8. Load content for each instance into the matching sync_root-path and check per host.
  9. Test all subdomains in the browser (curl optional).