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
5055internally, public80and443through 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
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
logs under each instance currently contains only client state files such as clients.json. No classic server logs are written there.
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.ymlthe same host must be listed ininstances[].hostsstand.
Multiple instances (mehrere Subdomains)
Typisches Example:
- Setze pro Instanz/Subdomain einen eigenen A-Record.
alpha-sync.example.com- A auf203.0.113.10beta-sync.example.com- A auf203.0.113.10cup-sync.example.net- A auf203.0.113.10
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 onserver.port- the port, for example5055routing.default_instance- optional fallback to a derived instance IDinstances- list of all instances withhostsinstances[].id- is automatically derived frominstances[].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.
If the binary is under
/syncserver/bin/syncserver the base is
/syncserver. This automatically creates the instance paths under
/syncserver/instances/<erster-host>/....
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
7. Set up a single instance
If you operate only one domain, one instance is enough. This is the simplest entry point.
sync_root .
- Define
hostsfor your instance. - Set
server.hostto127.0.0.1, if Nginx sits in front of it. - Set
server.portto a free internal port, for example5055. - Enter exactly one instance with your subdomain in
instances. - Optional: Set
routing.default_instanceto the derived instance ID (for examplealpha-sync) or leave it at""for no fallback.
/syncserver
|-- bin
| |-- syncserver
|-- config.yml
|-- instances
|-- alpha-sync.example.com
|-- sync_root
|-- sync_json
|-- logs
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.
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
routing:
default_instance: ""
instances:
- hosts:
- alpha-sync.example.com
- hosts:
- beta-sync.example.com
- hosts:
- cup-sync.example.net
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.1remain
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.comto the new subdomain - change each
proxy_set_header Host alpha-sync.example.comto 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.combecomesbeta-sync.example.com<id>.access.logand<id>.error.logas the schemaalpha-sync.access.logoptionally becomesbeta-sync.access.logalpha-sync.error.logoptionally becomesbeta-sync.error.log
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:
- one shared certificate with multiple
-dentries - 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
- The DNS records must already point to the server.
- Nginx must be reachable on port 80.
- The domain must be resolvable, otherwise Certbot cannot check.
- 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.ymland 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:
- is the sync server running locally?
- does Nginx answer on port 80/443?
- do the DNS records point to the server?
- is the SSL certificate valid?
- is the right host routed to the right instance?
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
Common mistakes
- 404 or wrong data: Hostname in Nginx or
config.ymldoes 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 tosync_jsonorlogs - If you work with Samba: the share must point to exactly this
sync_rootpath
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
<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
contentare not included in the series JSON sync_jsonis 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
Recommended overall workflow
Procedure for an instance
- Set a DNS-A record for this one subdomain.
- Create base paths under
/syncserver(at least/syncserver/bin). - Upload
syncserverandconfig.ymlto the server. - Adjust
config.ymlwith exactly one instance (hosts). - Configure nginx site for this one subdomain.
- Run Certbot for this subdomain.
- Start Sync Server as a systemd service.
- Load content into
/syncserver/instances/<host>/sync_rootand check the structure. - Test this subdomain in the browser (
curloptional).
Process for multiple instances
- Set a DNS-A record for each subdomain.
- Create base paths under
/syncserver(at least/syncserver/bin). - Upload
syncserverandconfig.ymlto the server. - Adjust
config.ymlwith multiple instances (hosts). - Configure a separate nginx site for each subdomain.
- Certbot with multiple
-dor run individually for each subdomain. - Start Sync Server as one systemd service (one process, multiple instances).
- Load content for each instance into the matching
sync_root-path and check per host. - Test all subdomains in the browser (
curloptional).