Environments & Repository Sources
This page covers the deployment-target model, environments, groupings, connection configuration, transport/security options, and the on-demand repository/storage sync integration.
Environments
Environments are user-defined deployment targets with no hardcoded taxonomy. Model whatever your pipeline needs (Development, QA, Staging, Production, DR, UAT, Sandbox, Demo). The Environments page shows one card per environment with its approval requirement, the environment it runs after, how many connections it holds (counting those its groupings bring in) and how many groupings are assigned. Opening an environment lists every connection in it with its engine and host; a connection that a grouping brought in says through that grouping, and leaves when the grouping is removed. From there you can:
- turn Requires Approval on or off (saved immediately); a release then runs there only with an approved approval request for that environment;
- choose the environment it Runs Only After (for example Production after Staging), so a release runs there only after a completed run in that environment since it was signed off. Loops are refused, and an environment that another one runs after cannot be deleted until that is changed;
- add existing connections (Add Connections) or create one already assigned to it (New Connection), and remove the ones added directly. A connection belongs to one environment, so adding one that is in another environment asks first and then moves it. A connection of another database type is listed as Other engine and cannot be added;
- assign or remove groupings (Add Grouping). Assigning one moves all of its connections into the environment, out of any other one; if that takes connections from another environment, or the grouping is already assigned elsewhere, you are asked first and told which connections move. Removing a grouping takes out the connections it brought in, unless another grouping still assigned to the environment covers them. The server makes the whole change in one transaction, and the message says how many connections moved;
- rename it. Every connection, script, release, approval request and compliance rule that names the environment is updated in the same transaction, so nothing is orphaned;
- delete it. Deletion is refused while any connection or script still references it, and the message says what is still using it; take those out first.
Every change is saved on the server before the page shows it. A refused change says which environment was not saved and why, for example "Staging" was not saved: That would make a loop: Staging after Production after Staging, and the page keeps the previous value. If the list cannot be loaded, the page says why and offers Retry.
Environments are stored in the platform database and persist across restarts; a fresh install is seeded with Development, Staging and Production, which you can rename or remove. Names are unique regardless of case and can be at most 50 characters. Adding or changing environments requires the Release Manager or Admin role; every role can view them.
Groupings
Groupings are logical clusters a connection can belong to, and unlike environment (one-to-one), a connection can be in multiple groupings. Model "all Postgres clusters", "tenant shard A", "us-east", etc., for set-wise deployment and reporting. Managed from the Groupings page (Release Manager or Admin; every role can view): each card shows how many connections the grouping holds and their database type, Add Grouping creates one, and opening a grouping offers Rename, Manage Connections and Delete. Groupings are stored in the platform database, so one with no members still persists. Names are unique regardless of case and can be at most 100 characters. Renaming cascades: every connection, environment and release that used the old name is updated in the same transaction, so membership is never orphaned. Deletion is blocked while any connection still belongs; deleting an unused grouping also removes it from any environment it was assigned to. Opening a grouping lists its connections with their engine, host, database and environment, click one to see that connection's details and test it without leaving the page. If the groupings cannot be loaded, the page says why and offers Retry; a refused change names the grouping and the reason.
Manage Connections opens Assign Connections: check a connection to add it, uncheck it to take it out. When the grouping is assigned to an environment, the dialog says so, because a grouping's connections belong to that environment:
- a connection that joins a grouping assigned to one environment moves into that environment. If it is in another environment, you are asked first, and the message after saving says where it moved. This applies on every path: Assign Connections, the connection form, bulk upload and the API;
- a connection of another database type than the environment is marked Other engine and cannot join;
- a grouping assigned to more than one environment moves nothing: joining it checks the database type, and the connection keeps its own environment;
- a connection cannot join two groupings that are assigned to different environments, or be placed in another environment in the same change;
- a connection that leaves a grouping stays in its environment, now as a direct member.
One database type per grouping and per environment. A grouping is deployed as a unit and a script is sent to every target in an environment, so both are limited to a single dialect. The first connection sets the type; connections of any other dialect are then greyed out with the reason, both when assigning to a grouping and when adding to an environment, and a grouping whose dialect clashes cannot be added to an environment. The server enforces the same rule for every path (the API, bulk upload and connection edits), returning 409 with the reason. Simcha DB Studio does not translate a script between dialects, the script text is sent to each target as written, so a mixed environment would deploy Oracle syntax to a MySQL target and fail there.
A grouping that already mixes types (created before this rule) is flagged on its card and in its detail with the types it holds, and cannot be assigned to an environment until it is split. Removing the connections that do not belong is always allowed, so a mixed grouping can be corrected in place.
Connection Configuration
Each environment is backed by one or more connections. The Connections page shows a card per connection with its engine's brand mark and its host, database, environment and groupings, each named; empty values are left out. Opening a card shows the same values with Test, Consumers, Edit and Delete. Hide Demo Connections takes the built-in demo connections out of your view, and Show Demo Connections brings them back.
Add Connection opens a form of the same name: pick the database type, supply host, port, database and credentials, optionally choose groupings and an environment, optionally mark Read-Only Connection, check reachability with Test Connection (Connected. or Could not connect: <reason>), and save. A new connection needs a password, unless Test Connection connects without one, as it does for a Redis, MongoDB, Cassandra or Elasticsearch server that has no authentication. Edit opens Edit Connection, saved with Save Changes; the database type of a saved connection cannot be changed, so add a new connection for another engine. Every save names the connection and, when it is refused, the reason. Connections persist through the shared /api/connections endpoint, so the IDE and Database DevOps see one list, saving once enables both interactive querying and unattended deployment.
Bulk Upload
Import many connections at once with Bulk Upload on the Connections page: choose or paste a CSV or JSON file (a CSV template is downloadable from the dialog). Columns are name, type, host, port, database, username, password, environment, groupings, ssl; only name and type are required, and multiple groupings are separated with ;. Rows are validated in the browser for an instant preview and again server-side per dialect. The import button says how many connections it will add. Each row succeeds or fails independently, one bad row never blocks the rest; afterwards the dialog lists how many connections were added and every skipped row with its reason. Reachability is not required at import time, so hosts the server cannot reach yet still import; use Test on each afterwards. Passwords are encrypted at rest exactly as with a single create.
Import from the IDE
The Manager and IDE keep independent connection lists. Import (beside Add Connection) lists IDE connections not already configured here (Select All picks every one shown) and creates an independent copy; if the list cannot be loaded the dialog says why and offers Retry; targets you already hold are never offered and re-imports are skipped, so import cannot duplicate. The reverse (DevOps → IDE) is available from the IDE sidebar.
Deleting a connection keeps its execution history: past runs stay in Executions and Metrics with their scripts, status and timing, and simply no longer link to a connection.
Read-Only Connections
Marking a connection Read-Only Connection makes the server refuse INSERT/UPDATE/DELETE/DDL and change-script runs before they reach the database, while leaving reads, EXPLAIN, and schema browsing available. The block is enforced application-side on top of the account's own grants, protecting even privileged logins; such connections carry a Read-only badge. Writes are recognised in each engine's own language: SQL and CQL statements that change data or schema (including a Cassandra BATCH), MongoDB operations other than reads (and an aggregate with $out or $merge), Redis commands other than reads (CONFIG GET and CLIENT LIST are reads; CONFIG SET is not), Cypher with CREATE, MERGE, SET, DELETE, REMOVE or DROP or a procedure that is not a known read, and Elasticsearch operations other than search, count, get and index information.
Environment Color-Coding
Each connection's environment renders as a risk-tiered badge, production red, staging amber, development/test green, everywhere a connection's environment appears (Connections list, execution history). Matching is case-insensitive and prefix-based ("Production", "prod", "PROD-eu" all tier alike).
Advanced: TLS, Timeouts & Credentials
The Advanced section tunes how a SQL-engine connection (PostgreSQL, MySQL/MariaDB, SQL Server) is opened and run; engines that don't use a setting ignore it.
- SSL / TLS, Disable, Require (encrypt, no cert verify), Verify CA / Verify Full (verify chain against the system trust store). Supabase starts at Require and PlanetScale at Verify Full, since both accept only TLS; other engines start at Disable. Existing connections retain prior behavior until changed.
- Connect Timeout (s), seconds to wait when opening.
- Statement Timeout (s), server-side cap per statement; blank for none. A strong rail for shared/production targets.
- TCP Keep-Alive, keeps idle sockets alive through firewalls/NAT.
- Save Password, on by default; when off, the password is verified at save but never stored (re-entered per connect, not persisted). Leave on for targets that deploy change scripts unattended.
SSH Tunnel (Bastion / Jump Host)
SSH Tunnel reaches databases only accessible inside a private network. Supply the bastion host and port, SSH username, the bastion's SHA256 host key fingerprint (required: the bastion is checked against it before anything is sent), and an SSH password or private key (PEM, optional passphrase); the Host/Port fields then resolve from the bastion's perspective (typically the database's private address). The tunnel serves both interactive querying and unattended execution and is torn down on disconnect. SSH credentials are encrypted at rest and masked in API responses, like database passwords.
Idle Auto-Disconnect (Operator)
CONNECTION_IDLE_DISCONNECT_MIN closes pooled connections (and any backing SSH tunnel) after N minutes idle; it is required, and 0 disables. Server-wide, not per-connection.
Connection Security
- Passwords encrypted at rest in the metadata database.
- TLS for all production/staging targets.
- Read-only where scripts must never write, app-enforced, atop account grants.
- Least-privilege, per-environment database users.
Deployment Pipelines
Development -> QA -> Staging -> Production -> DR
At each stage the release deploys to that environment's connections, the team validates, an optional approval gate guards promotion, and the same release (same scripts, versions, hashes) advances, no inter-stage modification.
Repository & Storage Sources
Sync migration scripts on demand from source control and object stores. Open Repo Sources in the Database DevOps navigation and choose Add Source: pick the provider, supply credentials, set Release Scripts Path, Ad-hoc Scripts Path and File Pattern, then Test Connection (Connected to ... or Could not connect: <reason>) and Add Source. Edit opens Edit Source, saved with Save Changes. Each card names the provider, branch and file pattern; opening a source shows its paths and the last pull in words, for example Last pull succeeded with when and what it did. Deleting a source keeps the scripts already pulled from it. Adding, editing and deleting sources requires the Admin role; any role that can create scripts can test a source and pull from it.
Credentials (tokens, app passwords, secret keys, connection strings, service-account JSON, external IDs) are encrypted at rest and returned masked. When you edit a source, leaving a masked field untouched keeps the stored secret, so you can change a path or pattern without re-entering the token.
Source Control
| Provider | Auth | Config |
|---|---|---|
| GitHub | PAT (repo scope) or OAuth token | owner/name, branch (default main) |
| GitLab | PAT (read_repository) | numeric Project ID, branch |
| Bitbucket | Atlassian account email and API token with Bitbucket scopes (repository read) | workspace/repo slug, branch |
| Azure DevOps | PAT (Code Read) | org, project, repo, branch |
Object Storage
| Provider | Auth | Config |
|---|---|---|
| AWS S3 | Access keys; or an IAM role ARN assumed via STS; or blank keys to use the ambient AWS credential chain (EC2/ECS role) | bucket, region, prefix |
| Azure Blob | Connection String | container, prefix |
| Google Cloud Storage | Service account key JSON; the account needs only Storage Object Viewer on the bucket | bucket, prefix |
| Dropbox | App key, app secret and OAuth2 refresh token (renewed automatically); or a short-lived access token | root folder |
Path & Pattern Model
Each source scopes two folders, Release Scripts Path (e.g. db/migrations/release) and Ad-hoc Scripts Path (e.g. db/migrations/adhoc), and a File Pattern. Subfolders are always included. Separate several patterns with commas (*.sql, *.js); matching ignores case. A pattern without / matches the file name anywhere below the folder; a pattern containing / matches the path inside the folder, and ** spans any number of subfolders (**/migrations/*.sql). Leave the pattern blank for *.sql.
On-Demand Sync
Open a source under Repo Sources and choose Pull Scripts. Choose the Type, Release (scripts join releases) or Ad-hoc (scripts run on their own), and the Environment of the new scripts, which every script needs. Optionally choose the Instance (only that environment's instances are listed, and when the source's file pattern names one kind of script, such as *.sql, only the instances that can run it, as the note under the field says) and its Database, which starts on the instance's own database; override the Branch (source-control providers) or the Path, which shows the source's path for the chosen type, or The whole source when none is set; and pull. Every matching file is imported as a Pending script named by its path inside the pulled folder, placed in that environment and target, and tagged with the provider, source name and mode. There is no background poll.
Pulls are incremental. Each imported script remembers its source file and a SHA-256 hash of its content, so pulling again skips files that have not changed, updates the script when a file changed (resetting it to Pending so the new content is reviewed; the script keeps its environment and target), and imports files that are new. The result reports imported, updated, unchanged and failed counts. A file that cannot be read is listed with its error and does not stop the rest of the pull. Pulling from an inactive source is refused.
Script Formats by Engine
Pulled or uploaded scripts are classified by extension first, then by content, and can only run against a compatible connection.
| Engine | Extension | How the script runs |
|---|---|---|
| PostgreSQL, CockroachDB, TimescaleDB | .sql | Split on ;; $$ function bodies stay whole. |
| MySQL, MariaDB | .sql | Split on ;; DELIMITER lines change the separator for procedures and triggers. |
| SQL Server | .sql | Each GO batch runs whole. |
| Oracle | .sql | Split on ;; PL/SQL blocks (BEGIN, DECLARE, CREATE ... PROCEDURE/FUNCTION/TRIGGER/PACKAGE) end at a line holding only /. |
| SQLite | .sql | Split on ;; trigger bodies run to their END;. |
| MongoDB | .js | mongosh-style db.<collection>.<method>(...) calls with literal arguments (inserts, updates, deletes, indexes, aggregate, bulkWrite, createCollection, createView, runCommand), plus ISODate, ObjectId, NumberDecimal/Int/Long and simple const/let values. Other JavaScript is rejected with the line number. |
| Redis | .redis | One command per line; # and // lines are comments. |
| Cassandra | .cql | Split on ;; BEGIN BATCH ... APPLY BATCH runs as one statement. |
| Neo4j | .cypher, .cyp | Split on ;. |
| Elasticsearch | .json | A JSON operation or array of operations (index create/delete, mappings, aliases, index, bulk, update, delete, update/delete by query, search, count, get). Bulk item errors fail the step. |
Comments are ignored when splitting, including separators that appear inside comments or quoted strings.
Failure Modes
- Auth failed, credential scope: GitHub needs
repo; GitLab needsread_repository. - No files, paths are relative to repo root (source control) or bucket root (storage); verify the pattern.
- Timeout, large-repo first sync; check connectivity from the server to the provider.
- Rate limiting, provider API caps on frequent sync; the error names the condition.