Sync Meraki Dashboard data into PostgreSQL and retrieve typed Meraki objects.
Project description
merakisync
Sync Meraki Dashboard data into PostgreSQL and retrieve typed Python objects — without configuring API or database connectivity in every script.
Table of Contents
- Requirements
- Installation
- PostgreSQL Setup
- Configuration
- Running Migrations
- Syncing Data
- Using the Library
- Environment Variables
- Supported Resources
- License
Requirements
- Ubuntu 22.04 or later — merakisync is developed and tested on Ubuntu. It may work on other Linux distributions or macOS, but these are not supported.
- PostgreSQL 13 or later
- A Meraki Dashboard API key (how to generate one)
- Python 3.11 or later (library install only — not required for the binary)
Installation
Binary — scheduled sync on a server
The binary is a self-contained executable that requires no Python installation. It is the recommended choice for running scheduled syncs on a server.
curl -LsSf https://raw.githubusercontent.com/nathanea05/merakisync/main/install.sh | sh
This installs merakisync to /usr/local/bin (or ~/.local/bin if you do not have write access to /usr/local/bin). On Linux servers without write access to /usr/local/bin, the script will prompt for sudo.
To pin a specific version:
curl -LsSf https://raw.githubusercontent.com/nathanea05/merakisync/main/install.sh | sh -s -- --version v1.0.0
To install to a custom directory:
curl -LsSf https://raw.githubusercontent.com/nathanea05/merakisync/main/install.sh | sh -s -- --install-dir /opt/bin
The binary provides the full CLI: merakisync init, merakisync migrate, and merakisync sync. It does not expose importable Python objects — use the library install below if you need those.
Updating the binary
Run the built-in update command. It downloads the latest binary, installs it in place, and applies any new database migrations in one step:
merakisync update
No service restart is required. If you need to pin to a specific version, use the manual flow instead:
curl -LsSf https://raw.githubusercontent.com/nathanea05/merakisync/main/install.sh | sh -s -- --version v1.0.0
merakisync migrate
Uninstalling the binary
Remove the binary and, optionally, the configuration file:
# Remove the binary (use the path reported during installation)
rm /usr/local/bin/merakisync
# or, if installed to ~/.local/bin:
rm ~/.local/bin/merakisync
# Optional: remove the configuration file
rm -rf ~/.config/merakisync/ # regular user
# or, if installed as root:
rm -rf /etc/merakisync/
The PostgreSQL database and schema are not touched by uninstall. Drop them manually if you no longer need the data:
DROP SCHEMA meraki CASCADE;
Python library — scripting and automation
Install via pip to import merakisync objects directly in your own Python scripts:
pip install merakisync
The pip install also provides the merakisync CLI command alongside the importable API.
To install from source:
git clone https://github.com/nathanea05/merakisync
cd merakisync
pip install -e .
PostgreSQL Setup
merakisync stores all data in a dedicated schema named meraki. Run all commands as a PostgreSQL superuser.
Database and user names: The examples below use
merakisyncfor both the database and the user. You can choose different names — just use them consistently in every command below and enter the same values when runningmerakisync init.
Remote databases: If PostgreSQL is not on localhost, update
pg_hba.confto allow connections from the host running merakisync and reload the service (pg_ctlcluster reloadorsystemctl reload postgresql).
Step 1 — open a superuser session on the postgres database
psql -d postgres
Homebrew on macOS: A fresh Homebrew install does not create a database matching your system username, so you must specify
-d postgresexplicitly.
Step 2 — create the database and user
CREATE DATABASE merakisync;
CREATE USER merakisync WITH PASSWORD 'your_password_here';
Step 3 — switch to the new database
The next commands must run inside the merakisync database, not postgres. Stay in the same psql session and run:
\c merakisync
If you are using a GUI client (VS Code SQLTools, DBeaver, etc.), close the current connection and open a new one targeting the merakisync database before continuing.
Step 4 — create the schema and grant privileges
CREATE SCHEMA IF NOT EXISTS meraki AUTHORIZATION merakisync;
GRANT USAGE, CREATE ON SCHEMA meraki TO merakisync;
ALTER DEFAULT PRIVILEGES IN SCHEMA meraki
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO merakisync;
ALTER DEFAULT PRIVILEGES IN SCHEMA meraki
GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO merakisync;
ALTER ROLE merakisync IN DATABASE merakisync
SET search_path = meraki, public;
Configuration
Setting up a scheduled sync? If you're deploying merakisync as a system service under a dedicated account — the recommended production setup — skip ahead to Setting up a service account. That section covers binary installation,
init, and migrations all in one place under the service account. You do not need to runmerakisync initas your own user first.
Run the interactive setup wizard:
merakisync init
The wizard will:
- Prompt for your Meraki API key and validate it against the Dashboard.
- Prompt for PostgreSQL connection details and test the connection.
- Offer to apply database migrations immediately.
- Save the configuration to
~/.config/merakisync/config.toml(mode600).
Running as root: Configuration is saved to
/etc/merakisync/config.tomlinstead, which is appropriate for system-wide deployments or scheduled jobs running under a service account.
The configuration file looks like this:
[meraki]
api_key = "your_meraki_api_key"
[database]
host = "localhost"
port = 5432
name = "merakisync"
user = "merakisync"
password = "your_password_here"
Running Migrations
Apply the database schema (creates all tables in the meraki schema):
merakisync migrate
This runs Alembic migrations up to the latest revision. It is safe to run multiple times — Alembic only applies revisions that have not already been applied.
Syncing Data
Sync everything
merakisync sync
Sync specific resource types
merakisync sync --organizations # or -o
merakisync sync --networks # or -n
merakisync sync --devices # or -d
merakisync sync --switchports
merakisync sync --uplinks
merakisync sync --uplink-usage
merakisync sync --dhcp-server-policy
merakisync sync --alerts
merakisync sync --l3-firewall-rules
merakisync sync --vlans
merakisync sync --ssids
Flags can be combined. For example, to sync only networks and devices:
merakisync sync -n -d
Logging
By default, merakisync logs at INFO level to stdout, which works well with cron and systemd.
merakisync sync --verbose # DEBUG level
merakisync sync --quiet # WARNING level and above only
Output is plain text with no colour codes, making it safe to redirect or capture in log files.
Scheduling recommendations
Run once daily at midnight UTC. This keeps data fresh and ensures UplinkUsage monthly totals remain accurate.
Important:
UplinkUsageuses an incremental sync strategy — each run queries only the delta since the last sync and accumulates the bytes onto the stored monthly total. Each query covers at most 14 days (the API's per-query maximum), but gaps up to 30 days are fully recoverable across multiple syncs. If more than 30 days pass between syncs, data beyond the 30-day lookback limit is unrecoverable and a warning is logged.
Setting up a service account
Run merakisync under a dedicated system account rather than your own user or root. The steps below set up that account, install the binary, and configure merakisync — all on the Ubuntu server that will run the scheduled sync.
Step 1 — create the service account
sudo useradd --system --shell /usr/sbin/nologin --create-home --home-dir /var/lib/merakisync merakisync
This creates a system account with no login shell and a home directory at /var/lib/merakisync. merakisync will store its config file there.
Step 2 — install the binary
curl -LsSf https://raw.githubusercontent.com/nathanea05/merakisync/main/install.sh | sudo sh -s -- --install-dir /usr/local/bin
Step 3 — run the setup wizard as the service account
sudo -u merakisync merakisync init
The wizard will prompt for your Meraki API key and PostgreSQL connection details, test both connections, and save the config to /var/lib/merakisync/.config/merakisync/config.toml.
Step 4 — apply database migrations
sudo -u merakisync merakisync migrate
Scheduling with systemd (recommended)
Step 1 — create the service unit
Create /etc/systemd/system/merakisync.service:
[Unit]
Description=merakisync — sync Meraki data to PostgreSQL
After=network.target
[Service]
Type=oneshot
User=merakisync
ExecStart=/usr/local/bin/merakisync sync
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Step 2 — create the timer unit
Create /etc/systemd/system/merakisync.timer:
[Unit]
Description=Run merakisync daily at midnight UTC
[Timer]
OnCalendar=*-*-* 00:00:00 UTC
Persistent=true
[Install]
WantedBy=timers.target
Step 3 — enable and start the timer
sudo systemctl daemon-reload
sudo systemctl enable --now merakisync.timer
Step 4 — verify
systemctl status merakisync.timer
You should see Active: active (waiting) and the next trigger time listed. To run a sync immediately and check the output:
sudo systemctl start merakisync.service
journalctl -u merakisync.service -n 50
Scheduling with cron
If you prefer cron over systemd, install the crontab for the service account:
sudo crontab -u merakisync -e
Add the following line:
0 0 * * * /usr/local/bin/merakisync sync >> /var/log/merakisync.log 2>&1
Create the log file and give the service account write access:
sudo touch /var/log/merakisync.log
sudo chown merakisync:merakisync /var/log/merakisync.log
Using the Library
Requires the Python library install (
pip install merakisync). The binary does not expose importable objects.
merakisync works with whichever connections you have available. You do not need both a Meraki API key and a database — configure only what you need:
| What you have | What you can do |
|---|---|
| Meraki API key only | Fetch typed objects directly from the Dashboard (source="meraki"). No database required. Useful for one-off scripts and tools that don't need persistence. |
| Database only | Query typed objects synced by another process or another team (source="database"). No API key required. Useful when you have read access to a shared merakisync database but no Dashboard credentials. |
| Both | Full functionality — run syncs to populate the database and query historical data from it. |
Configure your available credentials via the config file or environment variables and merakisync will use what is present.
from merakisync import Organization, Network, Device, Uplink, Switchport
# Retrieve from the database (default) — no API key needed
orgs = Organization.get(source="database")
networks = Network.get(org_id="123456", source="database")
# Filter by product type
switch_networks = Network.get(
org_id="123456",
source="database",
product_types_include=["switch"],
)
# Retrieve directly from the Meraki Dashboard API — no database needed
devices = Device.get(org_id="123456", source="meraki")
# Retrieve switchports for a specific device
ports = Switchport.get(serial="Q2AB-CDEF-1234", source="database")
# Access typed attributes
for network in networks:
print(network.name, network.product_types)
for device in devices:
print(device.serial, device.model, device.status)
Historical data (SCD2)
Most resources use SCD2 versioning. You can query the state of the network at any point in time:
from datetime import datetime, timezone
from merakisync import Device
# Devices as they were on March 1st
snapshot = Device.get(
org_id="123456",
source="database",
ts=datetime(2026, 3, 1, tzinfo=timezone.utc),
)
# All historical versions, not just current
all_versions = Device.get(org_id="123456", source="database", ts="all")
Getting a pre-configured API client or database session
from merakisync import get_dashboard, get_engine, get_session
# Pre-configured Meraki DashboardAPI instance
dashboard = get_dashboard()
raw = dashboard.organizations.getOrganizations()
# Pre-configured SQLAlchemy engine
engine = get_engine()
# Managed database session (commits on exit, rolls back on exception)
with get_session() as session:
result = session.execute(text("SELECT count(*) FROM meraki.device"))
Environment Variables
All configuration values can be supplied or overridden with environment variables. This is useful for containers and CI pipelines where writing a config file is not practical.
| Variable | Description |
|---|---|
MERAKI_API_KEY |
Meraki Dashboard API key |
MERAKISYNC_DB_HOST |
PostgreSQL host |
MERAKISYNC_DB_PORT |
PostgreSQL port (default: 5432) |
MERAKISYNC_DB_NAME |
Database name |
MERAKISYNC_DB_USER |
Database user |
MERAKISYNC_DB_PASSWORD |
Database password |
MERAKISYNC_LOG_LEVEL |
Log level: DEBUG, INFO, WARNING, ERROR (default: INFO) |
Environment variables take precedence over values in the config file.
Supported Resources
| Resource | Table | Source | Notes |
|---|---|---|---|
| Organization | meraki.organization |
Org-level | |
| Network | meraki.network |
Per-org | |
| Device | meraki.device |
Per-org | All product types |
| Switchport | meraki.switchport |
Per-org | MS switches only |
| Uplink | meraki.uplink |
Per-org | MX/Z devices |
| UplinkUsage | meraki.uplink_usage |
Per-org | Monthly bandwidth totals; data unrecoverable after 30-day gap |
| DhcpServerPolicy | meraki.dhcp_server_policy |
Per-network | Switch networks only |
| Alert | meraki.alert |
Per-org | Assurance alerts |
| L3FirewallRule | meraki.l3_firewall_rule |
Per-network | MX appliance networks |
| Vlan | meraki.vlan |
Per-network | MX appliance networks |
| Ssid | meraki.ssid |
Per-network | Wireless networks only |
All resources except UplinkUsage use SCD2 versioning — historical state is preserved when data changes. UplinkUsage stores cumulative monthly byte totals and updates in place.
License
merakisync is distributed under the terms of the MIT license.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file merakisync-0.1.7.tar.gz.
File metadata
- Download URL: merakisync-0.1.7.tar.gz
- Upload date:
- Size: 110.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0cb6d520c94430606defa0ad6f30424ed454738b08feb60f40181ef824a4234f
|
|
| MD5 |
414795a5e1efded8cc88c8721d5ad077
|
|
| BLAKE2b-256 |
4bdc2f94813e2d585240940cf9bea4726600ad34cf59fd1ce8dbfe7e4c362b65
|
Provenance
The following attestation bundles were made for merakisync-0.1.7.tar.gz:
Publisher:
release.yml on nathanea05/merakisync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
merakisync-0.1.7.tar.gz -
Subject digest:
0cb6d520c94430606defa0ad6f30424ed454738b08feb60f40181ef824a4234f - Sigstore transparency entry: 1770077398
- Sigstore integration time:
-
Permalink:
nathanea05/merakisync@32beb6ec681b2c95240d0f061907e14ecd2b7a5e -
Branch / Tag:
refs/tags/v0.1.7 - Owner: https://github.com/nathanea05
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@32beb6ec681b2c95240d0f061907e14ecd2b7a5e -
Trigger Event:
push
-
Statement type:
File details
Details for the file merakisync-0.1.7-py3-none-any.whl.
File metadata
- Download URL: merakisync-0.1.7-py3-none-any.whl
- Upload date:
- Size: 74.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af413b00f29b30a2befa9903a94b125c8ccb7441257140e6f013ede6c9ecba40
|
|
| MD5 |
b5a59d4f90c12a4b7a5c9e05c9541836
|
|
| BLAKE2b-256 |
6de74e94d88f12277e23aadcb9b3a84e10b0b53f373bd92a9dcfe4ce6d604e4f
|
Provenance
The following attestation bundles were made for merakisync-0.1.7-py3-none-any.whl:
Publisher:
release.yml on nathanea05/merakisync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
merakisync-0.1.7-py3-none-any.whl -
Subject digest:
af413b00f29b30a2befa9903a94b125c8ccb7441257140e6f013ede6c9ecba40 - Sigstore transparency entry: 1770077595
- Sigstore integration time:
-
Permalink:
nathanea05/merakisync@32beb6ec681b2c95240d0f061907e14ecd2b7a5e -
Branch / Tag:
refs/tags/v0.1.7 - Owner: https://github.com/nathanea05
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@32beb6ec681b2c95240d0f061907e14ecd2b7a5e -
Trigger Event:
push
-
Statement type: