Get started
Create a school, sync one table, and watch a change travel from the local database to the online one.
This tutorial takes you from an empty dashboard to one table syncing, and finishes with you editing a row in the school's local database and watching it appear in the online one.
You will configure a single table in a single direction. That is deliberately less than a real deployment needs, and it is the shortest path to seeing the whole system work.
What you need
- Two MySQL databases you can connect to: the school's on-site database and the online one.
- The same table present in both, with the same primary key. This tutorial uses
students. - A MySQL user that can
CREATE TRIGGERon the local database and write to the online one. - The agent executable for your platform, and the address of your monitoring server.
Create the school
In the dashboard, press Create School and fill in the dialog:
| Field | Value |
|---|---|
| Name | Riverside Academy |
| Code | RIV001 |
| Sync interval | 5 |
Leave Status as Active and the generated Setup token as it is.
Copy the setup token now and keep it next to this page — you need it twice, and it is shown only in this dialog. The server stores a hash of it, so once you save, the full value cannot be recovered.
Press save. Riverside Academy appears in the list with Last seen empty, because no agent has contacted the server yet.
Add one table
Open the school, go to the Sync tables tab, and press Add sync table:
| Field | Value |
|---|---|
| Table name | students |
| Primary key column | id |
| Apply order | 100 |
| Direction | Up |
| Create new rows | on |
| Update existing rows | on |
| Delete rows | off |
Up means the school's local database owns this table: changes made on-site travel to the
online copy, and not the other way round.
Create the capture triggers
Configuring the table tells the server what to expect. It does not yet capture anything — that is done by database triggers.
Still on Sync tables, press Setup SQL. The Capture setup script dialog opens with two tabs, one per database, because each database gets its own script:
| Tab | Run it on | Captures |
|---|---|---|
| Local database | The school's on-site database | The Up tables |
| Online database | The online database | The Down tables |
A trigger has to live in the database where the edit happens. students is Up, so it is
edited on the local database, and that is the only place its triggers are useful. Stay on the
Local database tab — it opens there by default. Check that the line above the script reads
Captures UP: students.
Get the script out of the dialog in whichever way suits the tool you run SQL with:
- Copy puts the whole script on the clipboard.
Paste it into SQLyog, HeidiSQL or any client that understands
DELIMITER, and execute it there against the local database. - Download .sql saves it as a file named after the school code and the side —
riv001-local-outbox.sqlhere. Pipe it into the MySQL command line client:
mysql -u root -p school_local < riv001-local-outbox.sqlRun it where the tab says
The script only works on the database its tab names. Running the local script on the online database puts the triggers on the wrong side, and edits made on-site are never captured.
This creates a sync_outbox table and three triggers on students, one each for insert,
update and delete (sync_students_ai, _au, _ad). Confirm they exist:
SHOW TRIGGERS LIKE 'students';The Online database tab is empty, and that is correct
You configured one Up table and no Down tables, so the online database has nothing to
capture. Its tab says no active DOWN tables are configured, and Copy and Download
.sql are disabled there. The online database gets no sync_outbox either, and does not
need one: an agent with nothing to capture never reads it.
Add a Down table, and the online database needs its script
The first Down table gives the Online database tab a script. Run it on the online
database before the online agent's next cycle. Until you do, every cycle there fails with:
! Table 'school_online.sync_outbox' doesn't existThe same message on the local side means the Local database script was never run there.
Start the local agent
The agent lives inside the school's application, next to the code that already talks to the
local database. On the school's machine, create a sync-agent/ folder in the application
root and put the executable in it:
C:\laragon\www\riverside_local\
├── app\
├── public\
├── sync-agent\
│ └── sync-agent-windows-x64.exe
└── .envOnly public/ is web-served, so the binary cannot be reached over HTTP. If the application
root is a git repository, add sync-agent/ to its .gitignore so the roughly 117 MB binary is
never committed.
The agent reads .env from the directory you run it from, and does not search parent
directories. You will run it from the application root, so it reads the application's own
.env. The database settings are already there, and the agent accepts Laravel's spelling of
them. Append the four monitoring settings to the end of that file:
# ...the application's existing settings, which the agent reuses:
DB_HOST=127.0.0.1
DB_USERNAME=root
DB_PASSWORD=your-password
DB_DATABASE=school_local
# Sync agent: which server to report to, and which school this agent belongs to
MONITORING_URL=https://monitoring.example.com
SCHOOL_CODE=RIV001
SYNC_TOKEN=the-token-you-copied
# Which database this agent sits on: LOCAL (the school's on-site database) or ONLINE.
# LOCAL captures the Up tables and applies Down changes. The online agent uses ONLINE.
SYNC_SOURCE=LOCALNo application on this machine?
The agent does not need one. Put the executable in any folder, create a .env beside it
holding the settings above, and run it from that folder. Use DB_USER and DB_NAME or the
Laravel names — the agent reads either.
SYNC_SOURCE is the one setting that tells the two agents apart. It has to match the database
in the DB_* settings and the Setup SQL tab you ran on that database — LOCAL here, because this agent
sits on the database where you ran the Local database script. It defaults to LOCAL, and
any value other than ONLINE counts as LOCAL, so a typo on the online side silently turns
that agent into a second local one.
Run it once, from the application root:
cd C:\laragon\www\riverside_local
.\sync-agent\sync-agent-windows-x64.exeIt prints one line and exits. Nothing has changed in the database yet, so the line reads
idle — possibly alongside counted 1, if the cycle also reported row counts.
Go back to the school's Overview. Last seen now holds a timestamp: the agent reached the server and authenticated. Last sync is still empty, because no data has moved.
Start the online agent
Do the same on the host that runs the online application: a sync-agent/ folder in its
application root, holding the executable for that host's platform. Its .env already points at
the online database. The monitoring settings you append are the same as on the local side,
except SYNC_SOURCE:
# ...the online application's existing settings, which the agent reuses:
DB_HOST=127.0.0.1
DB_USERNAME=root
DB_PASSWORD=your-password
DB_DATABASE=school_online
# Sync agent: same server, school and token as the local agent
MONITORING_URL=https://monitoring.example.com
SCHOOL_CODE=RIV001
SYNC_TOKEN=the-token-you-copied
# ONLINE: this agent sits on the online database.
# It captures the Down tables and applies Up changes, the reverse of the local agent.
SYNC_SOURCE=ONLINERun it once from that application root — on a Linux host:
./sync-agent/sync-agent-linux-x64It prints idle as well — the local side has captured nothing for it to apply.
This agent captures nothing itself, since there are no Down tables. Its job in this
tutorial is to apply what the local side sends.
Change a row and follow it
On the school's local database, edit one row:
UPDATE students SET last_name = 'Fernandez' WHERE id = 1;The trigger has already written that change to sync_outbox. Now run the local agent again, from the local application root:
.\sync-agent\sync-agent-windows-x64.exeThe line now reads UP pushed 1. The change is on the server, queued for the other side.
Run the online agent, from the online application root:
./sync-agent/sync-agent-linux-x64It reads UP applied 1. Query the online database and the row has the new surname:
SELECT last_name FROM students WHERE id = 1;Confirm it in the dashboard
Open the school's Overview. Last sync now holds a timestamp, and Table Monitoring
lists students with a row count from each side.
The Sync history tab has runs in it, each marked Success. One is the push from the
local agent; one is the apply from the online agent.
What you have now
One table syncing in one direction, with two agents you run by hand. A change made on-site reaches the online database the next time both agents run.
Three things stand between this and a working deployment:
- The agents run once per invocation. Something has to invoke them repeatedly.
- Only
studentssyncs, and only upward. Every table needs configuring, per direction. - Only the local database captures. Re-run the setup SQL after every change to the table list.
Triggers only capture what happens after they exist
Rows that were already in students before you ran the setup SQL have never been captured,
so the two sides can hold different data even though sync is working. Request a
backfill for the table to send them.