synq.json
The declarative deploy config for Synq — like vercel.json or railway.toml. Declare framework, build, runtime, env, domains, routes, resources, services and monorepo layout, and Synq deploys the same way every time.
synq.json is a repo-root file that tells Synq how to build, run, route, and scale a
project. It mirrors what you'd otherwise set in the deploy wizard,
so a repo becomes "ready to ship on Synq" and auto-deploy on push
and synq deploy become deterministic — no wizard needed.
Authoritative overlay
Synq auto-detects your stack first, then applies every field present in synq.json on
top. Fields you omit keep their detected value. So a good synq.json is small — declare
only what you want to override or pin, not the whole detected config.
Add the $schema line for editor autocomplete and inline validation:
{
"$schema": "https://synquic.tech/synq.schema.json",
"framework": "nextjs",
"port": 3000
}Validate it any time with the CLI:
synq config init # scaffold a starter synq.json
synq config validate # check it against the schemaIt's JSON, not JSONC — no comments, no trailing commas.
Build
Prop
Type
Runtime
Prop
Type
Persistent storage
Everything a container writes is discarded when the next version replaces it. volumes
names the paths to keep.
A bare path is relative to the app; full compose syntax works too, so you can point a mount at a specific volume name or a host directory:
{
"volumes": ["storage", "uploads:/app/public/uploads", "/srv/data:/app/var"]
}Omit the field to inherit the framework's own defaults (a Laravel app keeps storage/
with no configuration). Declare [] to turn persistence off entirely — that is
different from omitting it.
Named volumes are scoped per project, so two projects that both declare storage never
share one. On a bare (non-container) deploy the same paths are kept outside the release
directory and symlinked in.
Compose services declare their own mounts under services[].volumes.
Environment variables
env is an object. A value is a plain string, or { "value", "secret" } to mark a secret
(encrypted at rest — never returned to the client afterward). These declarations are active
automatically; a value saved in the dashboard or CLI for the same key overrides the repository default.
{
"env": {
"NEXT_PUBLIC_URL": "https://app.acme.com",
"DATABASE_URL": { "value": "postgres://…", "secret": true }
}
}Domains
domains is an array of hostnames or objects. A bare label ("myapp") asks for a free subdomain, which on a
Synq install is myapp.<your base domain> (SYNQ_APPS_BASE_DOMAIN); a dotted hostname ("app.acme.com") is
a custom domain.
{ "domains": ["app.acme.com", { "domain": "api.acme.com", "port": 8080, "type": "custom" }] }Prop
Type
Routes
routes reproduces vercel.json-style routing, compiled to the reverse proxy at deploy.
Prop
Type
Resources
CPU/memory limits for the project's containers — a named tier, or explicit values (which become
the custom tier). See Sleep mode & resources.
Self-hosted defaults to no limits
On a self-hosted instance the default is unlimited — the machine is the ceiling, because it's
your hardware. Set a tier or explicit values only when you deliberately want to cap a container.
A non-zero value is validated against the target machine's real capacity, so a 64 GB box can
hand a container 64 GB.
Prop
Type
{
"resources": { "cpuCores": 4, "memoryMb": 8192 }
}Services (compose)
Declaring services makes the project a multi-service (Docker) project — see
Compose / multi-service. Each entry requires a name.
Already have a compose file? Don't re-declare it here — point composePath at it and leave
services out. That works for a compose file anywhere in the repo (deploy/docker-compose/),
under any filename (stack.yml), and keeps the file the single source of truth: its services are
re-read on every deploy, and build: contexts resolve relative to it, exactly as compose does.
{ "composePath": "deploy/docker-compose/docker-compose.yml" }{
"services": [
{
"name": "web",
"build": ".",
"buildArgs": { "NEXT_PUBLIC_API_URL": null },
"ports": ["3000"],
"exposed": true,
"domain": "app.acme.com"
},
{
"name": "db",
"image": "postgres:17",
"volumes": ["pgdata:/var/lib/postgresql/data"],
"env": { "POSTGRES_PASSWORD": { "value": "…", "secret": true } },
"restart": "unless-stopped"
}
]
}Prop
Type
dependsOn takes service names such as ["db"], without a port. It controls container startup
order, not database availability during an image build. For ENOTFOUND db during next build,
see Database access during a Next.js build.
Top-level env is available to Dockerfile builds as well as running containers. Use
buildArgs when a service needs an explicit override or when you want the config to document
which Dockerfile arguments it consumes. A service's own env is runtime-only and is never shared
with sibling builds. Keep runtime-only credentials in service env where possible: Docker build
arguments are not a secret-storage mechanism and can be retained in image metadata or build cache.
A compose file's own limits are honored too — Synq reads mem_limit / cpus and
deploy.resources.limits.{memory,cpus} off each service and applies them over the project-wide
setting.
Monorepo
monorepo overrides Synq's detected sub-apps. Entries in apps[] are matched to detected
sub-apps by rootDirectory and override their build settings — they don't declare apps from
scratch (the detector finds the apps).
Each override must have a different normalized rootDirectory. Declaring a web
process and a worker at the same root is not a supported multi-app declaration.
synq config validate rejects duplicate roots; source scans report unmatched
apps and workspace overrides in the configuration diagnostics.
Create a monorepo through the repository import flow, which supplies the detected
app metadata. The API/SDK also accepts that metadata as monorepoApps. Setting
projectType: "monorepo" (or CLI project create --type monorepo) without it now
fails before creating a project. A local CLI import scans the workspace first.
For independent processes without containers, use separate projects pointed at
the required roots and start commands.
{
"monorepo": {
"workspace": { "packageManager": "pnpm", "prepareCommand": "pnpm install && pnpm codegen" },
"apps": [
{ "name": "web", "rootDirectory": "apps/web", "framework": "nextjs", "port": 3000 },
{ "name": "api", "rootDirectory": "apps/api", "framework": "hono", "port": 8080 }
]
}
}Prop
Type
Not supported yet
sleepMode, monorepo sharedPaths, and per-app domain/env/exposed are accepted by the
validator but not applied — leave them out. Set sleep mode and per-app domains in the
dashboard for now.
How it maps
synq.json | Deploy wizard / API |
|---|---|
framework, packageManager, *Command, outputDirectory, buildImage, productionPaths, rootDirectory | Build settings |
port, productionMode, runtime | Runtime settings (hasServer, runtime isolation) |
volumes | Configuration → Persistent storage |
env | Environment variables (seeded as editable rows) |
domains | Public endpoints / custom domains |
routes | Reverse-proxy routing (routingConfig) |
resources | Machine Power (resource tier / custom sizing) |
services | Compose services |
monorepo | Monorepo sub-apps + workspace |
See also
MCP
Let AI clients like Claude drive your Synq server as tools, over the Model Context Protocol. The endpoint on your panel's host, OAuth or token sign-in, what agents can do, and how to limit and review them.
App catalog JSON
The complete field reference for a Synq App definition, the JSON that turns a set of containers into a one-click install, with generated secrets, a curated settings form, and a connection card.