Deploy a model
Deploy a PyRel model to a Snowflake schema so its outputs are accessible as regular Snowflake objects: tables, views, or dynamic tables. This guide shows how to configure the target schema and role, validate and run the deployment in the CLI, inspect and query the model outputs in Snowflake with SQL, and troubleshoot common issues.
What deploying a model does
Section titled “What deploying a model does”The outputs of a PyRel model are the concepts and relationships derived from its source tables via any of the supported reasoners. By mapping every concept to an object with a column for each of its properties, and every relationship to an object with a column for each of its fields, model outputs can be fully represented using familiar Snowflake objects. This makes deployed model outputs appear like any other governed data source in your organization.
To deploy, you pick a target Snowflake schema where the objects derived by your model should live.
As long as your model remains the same, the generated schema stays stable so others in your organization can depend on it.
PyRel also creates an additional meta schema (<target name>_META by default) to store model state and intermediate results that others should not depend on.
Deploying a model to a target schema does not move the model’s source tables. Source tables can come from any other schema and remain completely unaffected.
Each model output needs a refresh schedule, which tells PyRel when to refresh that output. You can configure a schedule to run periodically, or to run only when you trigger it manually or from an external pipeline.
All Snowflake objects (tables, views, procedures, and tasks) that PyRel creates on your behalf carry a relationalai_managed = 'true' tag.
PyRel throws an error if a deployment would override an existing object that doesn’t carry this tag.
A single model can be deployed to an arbitrary number of target schemas simultaneously. This is typically used to create separate development and production environments, or to support multiple tenants with isolated data sources. PyRel fits into your existing development and deployment processes.
Create your first deployable model
Section titled “Create your first deployable model”In this section, you’ll set up a new, deployment-ready PyRel model. If you already have a model you want to deploy, skip to the next section.
- You have a working PyRel installation and a working Snowflake connection.
- You have access to a Snowflake account where the RelationalAI Native App is installed.
The steps below use the starter model to validate your deployment setup end to end.
-
Initialize a new model
In your working directory, run:
Terminal window rai models init [your-model-name]For command details and options, see
rai models init.This creates a folder
your-model-namewith a starter model andraiconfig.yaml. The generatedraiconfig.yamldemonstrates a best practice: use separatedevandprodprofiles inraiconfig.yaml. By switching profiles, you can keep using the samerai models deployflow, whether you are developing against an isolated schema with test data, or releasing to a production schema widely used throughout your organization. -
Configure your development connection in
raiconfig.yamlIn the
devprofile, fill in the connection fields for your Snowflake account and authentication method. You will also need to configure which warehouse to use for SQL execution. For example:default_connection: sfconnections:sf:type: snowflakeaccount: my_org-my_accountwarehouse: my_warehouserai_app_name: relationalai # name of the RAI app in your Snowflake accountauthenticator: externalbrowser # SSO via browser; for PAT or JWT see README.mduser: you@example.com -
Set the target schema and refresh schedule in
raiconfig.yamlAgain for the
devprofile, fill in themodelanddeploymentsections. Theschemashould be unique to the model. No other models or applications should modify the schema, to avoid conflicts and unexpected results.model:path: model.py # path to the model entry point scriptdeployment:schema: MY_DATABASE.MY_DEV_SCHEMA # fully-qualified <DATABASE>.<SCHEMA>role: MY_DEPLOYMENT_ROLE # Snowflake role used to deploy and refresh the model# auto_deploy: false # set to true to auto-deploy the model before queriesschedules:standard:interval_s: 0 # run on deploy, but do not refresh on a fixed intervaloutputs:schedule: standard # assign all outputs to the `standard` schedule -
Load some sample data
The starter model includes a script to load a few rows of test data that you can use to validate everything is working correctly:
Terminal window python load_data.pyIf you have some source tables already, just skip this step and update
model.pyto use those. -
Deploy from the CLI
Before your first deployment, you can validate your configuration and database connectivity with
rai connect.With the data loaded, it’s time to trigger the deployment. In the model directory (the one containing your
raiconfig.yaml), pick thedevprofile and run:Terminal window RAI_PROFILE=dev rai models deploy --waitFor command details and options, see
rai models deploy.--waitmakes the deploy command wait for the initial refresh of the model to finish. -
Inspect the deployed model
Once the deployment and the initial refresh complete, your model outputs will be visible in the target schema. Use the Snowflake UI or CLI to inspect the target schema. It should now contain several dynamic tables corresponding to the
ConnectionandStationconcepts from the starter model.You can also query a deployed model through PyRel directly. For the starter model, run:
Terminal window python queries.pyThis runs a few example queries. Note that PyRel queries return dataframes and are not themselves visible in the target schema.
You can configure the prod profile in the same way.
When you’re ready to deploy to production, run RAI_PROFILE=prod rai models deploy.
If you need to reset a development deployment and clean up its managed objects, see rai models teardown.
Make an existing model deployable
Section titled “Make an existing model deployable”In this section, you’ll update your existing raiconfig.yaml so an existing model is ready for deployment.
If you’re starting from scratch, see the previous section.
- You have a working PyRel installation and a working Snowflake connection.
- You have access to a Snowflake account where the RelationalAI Native App is installed.
- You know which Snowflake schema to target for this workflow and can inspect that location in Snowflake.
- You already know how to create a
Modeland run a query.
The steps below focus only on the deployment-specific configuration you need to add.
Eventually, any existing PyRel model will be deployable. However, the following limitations currently apply:
- Deployment is currently supported only for models using rules-based reasoning.
- Existing PyRel queries must be strictly separated from the model definition.
Your existing raiconfig.yaml will already have one or more Snowflake connections configured, which you can keep using.
If you haven’t already, consider adding separate dev and prod profiles as a best practice.
The steps below apply to any profile.
-
Specify the entry path for your model
Make sure that your
modelsection specifies where your model can be found. This is done via thepathfield, which can point to a Python file or module.model:path: model.py -
Set the target schema in
raiconfig.yamlAdd a
deploymentsection specifying the target schema and role.deployment:schema: MY_DATABASE.MY_DEV_SCHEMA # fully-qualified <DATABASE>.<SCHEMA>role: MY_DEPLOYMENT_ROLE # Snowflake role used to deploy and refresh the model# auto_deploy: false # set to true to auto-deploy the model before queries# suspend_after_mins: 60 # auto-suspend model refresh, useful in developmentThis tells PyRel where to deploy the model and which Snowflake role to use for deployment and refresh tasks. If you omit
deployment.role, PyRel uses the connection role or current session role. -
Define a refresh schedule
Every model output must be assigned to a schedule. Extend your
deploymentsection to create a schedule and use it as the default for all model outputs. Schedules are explained in more detail below.deployment:schema: MY_DATABASE.MY_DEV_SCHEMArole: MY_DEPLOYMENT_ROLEschedules:standard:interval_s: 30 # outputs on this schedule refresh every 30soutputs:schedule: standard # set the `standard` schedule as the default -
Deploy from the CLI
Select the right profile and trigger the deployment.
Terminal window RAI_PROFILE=dev rai models deployFor command details and options, see
rai models deploy.
Configure deployed model refresh schedules
Section titled “Configure deployed model refresh schedules”Most models are dynamic. Their outputs need to be maintained in response to changes to the source tables.
One or more schedules can be configured for a deployed model. Each schedule installs a Snowflake task in the meta schema that orchestrates all the involved reasoners to refresh the schedule’s outputs as the source data changes.
Every output must be assigned to a schedule.
The simplest way to do this is to set a default schedule for all outputs in the deployment section of your raiconfig.yaml:
deployment: schema: MY_DATABASE.MY_DEV_SCHEMA role: MY_DEPLOYMENT_ROLE
schedules: standard: interval_s: 30 # outputs on this schedule refresh every 30s
outputs: schedule: standard # set the `standard` schedule as the defaultinterval_s must be either 0 or at least 10.
Use 0 when you want a task that can be triggered manually or by an external pipeline instead of running on a fixed interval.
By default, PyRel runs each schedule once during deployment, even when interval_s is 0.
Set run_on_deploy: false when you want to deploy the model without triggering a refresh:
deployment: schema: MY_DATABASE.MY_DEV_SCHEMA role: MY_DEPLOYMENT_ROLE
schedules: external_pipeline: interval_s: 0 run_on_deploy: false
outputs: schedule: external_pipelineFor development configurations, it can be useful to automatically suspend schedules after some time to prevent lingering tasks from test deployments:
deployment: schema: MY_DATABASE.MY_DEV_SCHEMA role: MY_DEPLOYMENT_ROLE suspend_after_mins: 15 # suspend schedules after 15 minutes
schedules: standard: interval_s: 30
outputs: schedule: standardSimple models often use the same schedule for all outputs. For more sophisticated deployments, it can be useful to refresh different parts of the model at different intervals, or to trigger refresh from existing data pipelines. Schedule names must start with a letter and can contain only letters, numbers, and underscores.
Configure output materialization
Section titled “Configure output materialization”By default, PyRel creates deployed outputs as dynamic tables when it can.
You can choose the default output type for the deployment, and you can override individual outputs.
Valid output types are dynamic_table, table, and view.
Set the default output type under deployment.outputs:
deployment: schema: MY_DATABASE.MY_DEV_SCHEMA role: MY_DEPLOYMENT_ROLE
schedules: standard: interval_s: 30
outputs: schedule: standard type: table # the default output typeOverride specific outputs when one part of the model needs a different materialization:
deployment: schema: MY_DATABASE.MY_DEV_SCHEMA role: MY_DEPLOYMENT_ROLE
schedules: standard: interval_s: 30
outputs: schedule: standard type: dynamic_table overrides: - objects: - ACCOUNT_SCORE type: table # override this output to `table`PyRel may fall back to a table when an output is configured as a dynamic table but cannot be represented as one. For example, this can happen when the output does not depend on other Snowflake objects.
Deploy from the CLI
Section titled “Deploy from the CLI”After your model is correctly configured, you can trigger a deployment from the CLI.
From the model directory that contains raiconfig.yaml, pick a profile and run:
RAI_PROFILE=dev rai models deploySee rai models deploy for command details and options.
This will perform basic validation of your configuration and Snowflake connection. Refer to the troubleshooting section below if you run into issues.
Monitor a deployment from SQL
Section titled “Monitor a deployment from SQL”Once a deployment succeeds in the CLI, PyRel creates the target schema and meta schema if they don’t exist yet.
By default, the meta schema is <target name>_META.
You can override that default with deployment.meta_schema.
PyRel then starts any refresh schedule whose run_on_deploy setting is true.
Inspect the target schema to verify this.
-
Check the target schema
SHOW SCHEMAS LIKE '<YOUR_TARGET_SCHEMA>' IN DATABASE <YOUR_DATABASE>;Replace
<YOUR_TARGET_SCHEMA>and<YOUR_DATABASE>with the target schema you set fordeployment.schema. -
List installed objects
During deployment, the PyRel compiler decides which type of Snowflake object to use for each output. It may choose regular tables, views, or dynamic tables. This can also be configured on a per-output basis.
SHOW OBJECTS IN SCHEMA <YOUR_DATABASE>.<YOUR_TARGET_SCHEMA>;You should see objects corresponding to each of the concepts and relationships in your model.
-
Check the model’s refresh status
The meta schema exposes a
REFRESH_STATUSprocedure that returns one row per refresh schedule, showing each schedule’s current status.REFRESH_STATUSis the first place to check when a model is not refreshing as expected.CALL <YOUR_META_SCHEMA>.REFRESH_STATUS();You should see a row for each schedule. The
statuscolumn can return:Status Meaning DEPLOY_RUNNINGA deployment is currently updating the model. DEPLOY_FAILEDThe latest deployment failed. NEVER_RUNThe schedule exists but has not run yet. RUNNINGA refresh is currently running. SUCCEEDEDThe latest completed refresh succeeded. FAILEDThe latest completed refresh failed. The errorcolumn contains the failure message.
If any of these steps fails or Snowflake does not show the expected objects, continue to the troubleshooting section below.
Understand output consistency
Section titled “Understand output consistency”The outputs of a deployed model are eventually consistent with their source data. After a source table changes, deployed outputs will continue to show earlier results until a refresh reads the change and finishes successfully.
Within a refresh run for one schedule, PyRel updates outputs progressively in dependency order: dependencies first, then the outputs that depend on them. Outputs that do not depend on each other may refresh in parallel.
Refresh runs are not snapshot-isolated across all reads. If a refresh reads the same source table more than once, and the table changes between those reads, the refresh may read different versions of that table.
Different refresh schedules run independently. Dependency order is enforced within a schedule, but not between schedules. If dependent outputs are assigned to different schedules, queries may see a mix of older and newer output data while those schedules are refreshing.
For example, consider this model, with one source table S and three model outputs A, B, and C:
If A, B, and C are assigned to the same schedule, a refresh submits updates for A and B first.
After both finish, the refresh updates C.
If A, B, and C are assigned to different schedules, there is no cross-schedule ordering guarantee.
For example, a query may see A after it has refreshed, B before it has refreshed, and C before it reflects both updates.
Branch, collaborate, and merge
Section titled “Branch, collaborate, and merge”After you deploy a model, you manage it through a workflow built around the current model, its op log history, and any branches you create from it. The next sections explain how deployments are tracked, how to switch to the right model, and how to branch, collaborate, merge, and tear down models safely.
How deployments are tracked
Section titled “How deployments are tracked”- You already have a model deployed to a Snowflake schema.
- Op log recording is enabled. It is on by default, and the branching, pull, and merge flows below rely on it.
Every rai models deploy does two things.
It installs your model’s resources and outputs into the target schema, and it records the difference between your model and the previously deployed version as a series of operations in the schema’s op log.
The op log is your model’s history: an ordered record of how the model has changed from one deploy to the next. When a deploy records changes, PyRel tells you how many:
Recorded 3 change(s) to the snowflake oplog.You never edit the op log directly. The rai models commands maintain it for you.
Each model and branch keeps its own op log.
That history is what the rest of this workflow builds on: deploy appends to the current model’s op log, branching forks a new branch from the parent’s history, and merging appends a branch’s changes back onto its parent’s op log.
Select the model you want to manage
Section titled “Select the model you want to manage”- You’re comfortable with
rai models deployand with thedeployment.schemasetting inraiconfig.yaml.
To branch, pull, merge, or tear down a model, first make sure you’re pointed at the right one.
Every command that manages a model acts on your current model: the schema named by deployment.schema in raiconfig.yaml.
rai models deploy, branch, pull, merge, and teardown all target it.
Use rai models list to see the models that exist, and rai models switch to choose the one you want to work on:
rai models listrai models switch MY_DATABASE.MY_DEV_SCHEMArai models switch validates that the schema is a provisioned model, then rewrites deployment.schema in your raiconfig.yaml, preserving its comments and layout.
rai models list shows each model’s name, its parent if it is a branch, and its type: a base model, a live branch model, or a static branch model.
Create a branch
Section titled “Create a branch”- Your current model is already deployed to a Snowflake schema.
A branch is a fork of your current model into a brand-new Snowflake schema. Creating one is lightweight: it is a zero-copy fork that duplicates none of the parent’s data. The branch starts from the parent’s deployed state and history, then evolves on its own: you can deploy changes to it without touching the parent.
Because the parent is your current model, the schema you branch from must already be deployed.
-
Pick the model to branch from
Confirm your current model, switching if needed:
Terminal window rai models switch MY_DATABASE.MY_DEV_SCHEMA -
Create the branch
Terminal window rai models branch EXPERIMENT_XFor command details and options, see
rai models branch. -
Switch into the branch and work in it
Terminal window rai models switch EXPERIMENT_XEdit your model and deploy as usual:
Terminal window rai models deployChanges land in the branch’s schema. The parent is untouched.
Choose a live or static branch
Section titled “Choose a live or static branch”Branches differ in how they track the parent as the parent changes:
- Live branches (the default) stay connected to the parent. When you run
rai models pull, the parent’s latest changes are reconciled into your branch automatically. - Static branches (
rai models branch --static) are frozen at the fork point. A plainrai models pulldoes not bring in the parent’s later changes. You incorporate them explicitly, when you choose to, withrai models pull --from-parent.
Choose a live branch for short-lived work that should keep pace with the parent, and choose a static branch when you want a stable base that only changes when you ask it to.
Collaborate through a shared model
Section titled “Collaborate through a shared model”- Op log recording is enabled for the shared model. It is on by default, and
rai models pullandrai models deployuse it to reconcile shared changes.
Several developers can work against the same deployed model. The op log becomes the shared deployment history because each deploy appends to it.
A model has two parts, and they stay in sync in different ways:
- The part you own: the model source you author. You can split it across as many files as you like and name them however you want. The op log records your changes to these entities too, but PyRel does not sync your source for you: you share it and resolve any conflicts the usual way, through version control (for example,
git pull). When you deploy owned changes, PyRel reminds you to commit them to source control. - The shared part: entities contributed through the op log, including by your collaborators. PyRel projects these into a generated file named
shared_model.py, written alongside your source. This file becomes part of the same model. It is not a separate, parallel model. PyRel wires it in automatically ondeployandpull.rai models pullkeeps it current by regenerating it from the op log, so shared changes are reconciled for you automatically. You normally let PyRel maintain this file, but you can also edit it directly: as long as your copy is up to date, your edits are deployed and pushed to the op log just like a change to your own source.
In short, rai models pull reconciles shared changes for you automatically, while owned changes are yours to manage in version control.
The one exception to that automatic sync: if you have hand-edited shared_model.py, neither rai models pull nor rai models deploy will overwrite it unless you pass --force - both regenerate the file and refuse to clobber hand edits.
With --force, your current file is backed up to a timestamped shared_model.py.<timestamp>.bak first.
To reference shared entities from your own code:
from shared_model import build_shared
shared = build_shared(m) # shared.Person, shared.orders, ...A typical collaboration loop:
-
Pull before you start
Terminal window rai models pullFor command details and options, see
rai models pull. -
Make your changes and deploy
Terminal window rai models deployIf a collaborator deployed since your last pull, the deploy is refused with a message telling you to pull first. Run
rai models pulland deploy again to get back in sync.
For a static branch, a plain rai models pull only syncs the branch’s own changes.
To incorporate the parent’s latest into the branch, run rai models pull --from-parent.
Merge a branch back
Section titled “Merge a branch back”When a branch’s changes are ready, merge promotes them back into the parent and retires the branch.
A branch must sit on top of the parent’s latest changes before it can merge. In practice that means catching the branch up on the parent and re-deploying it first.
-
Switch to the branch
Terminal window rai models switch EXPERIMENT_X -
Catch up on the parent and rebase
Terminal window rai models pullrai models deploy -
Merge
Terminal window rai models mergeFor command details and options, see
rai models merge.
rai models merge diffs the branch against its parent, appends the branch’s net changes to the parent’s op log, installs the merged model into the parent schema, and retires the branch.
The parent is resolved automatically from the branch’s lineage, so you never name it.
A retired branch can no longer receive deploys.
Add --delete to also drop the retired branch’s schema and op log in the same step:
rai models merge --deleteTear down a model or branch
Section titled “Tear down a model or branch”rai models teardown drops the current model or branch and everything in it: its schema, its meta schema, and its op log.
By default, teardown is a dry run: it prints exactly what would be dropped and stops.
Re-run with --force to actually perform it.
rai models teardown # dry run: shows what would be droppedrai models teardown --force # actually drop itAfter tearing down a branch, deployment.schema is reset to the branch’s parent.
After tearing down a root model, deployment.schema is cleared.
Your local files (your model source and shared_model.py) are left untouched.
Teardown protects you from losing work you may not have meant to:
- It refuses to discard a branch that has unmerged changes. Override with
--allow-unmergedto discard them. - It refuses to orphan downstream branches. Override with
--allow-childrento orphan them.
If a deploy, pull, merge, or teardown step does not behave as you expect, use the troubleshooting section below to narrow down the cause.
Troubleshoot
Section titled “Troubleshoot”Deployment and model management commands can fail for several reasons. Start with the quick diagnostics below, then use the issue list to narrow down the most likely cause.
Many deployment issues come from missing or invalid model configuration or Snowflake connection settings. Start by running:
rai doctorIf the issue still looks configuration-related, also run:
rai config explainWhen you need a deeper look or want to share diagnostics with RelationalAI support, generate a doctor report. It bundles the resolved configuration, connection and privilege checks, deployment state, and recent run traces into a single zip:
rai doctor reportThis writes doctor_report_<timestamp>.zip to the current directory. Attach that zip to your support request. See rai doctor report for command details and options.
If you deploy programmatically by building a config in Python and passing it to Model(config=...) rather than using a raiconfig.yaml file, run the report from Python and pass the same config, so it reports on the configuration your deployment actually used:
from relationalai.tools.cli.doctor_report import run_doctor_report
run_doctor_report(config=config)Inside a Snowflake Notebook, use download_doctor_report instead. It generates the report and renders a button to download the zip:
from relationalai.tools.cli.doctor_report import download_doctor_report
download_doctor_report(config=config)No objects show up in the target schema
If rai models deploy succeeds but the target schema still looks empty, you’re most likely looking at a different schema than the one the model deployed to.
Verify that deployment.schema is correct and that you’re using the expected profile.
Snowflake raises a privilege error during model refresh
If deployment succeeds but the model refresh task fails with a privilege error, the deployment.role you configured most likely does not have permission to execute Snowflake tasks. Confirm the target schema and access configuration with your Snowflake admin or with RelationalAI support.
Source tables are not visible in the target schema
Only model outputs are deployed into the target schema. Source tables stay in their original schemas and aren’t affected by deployment. Check your tables configuration to confirm which schema your source tables come from.
Unexpected objects show up in the target schema
The PyRel compiler can choose tables, views, or dynamic tables for a given output.
You can also override that behavior in raiconfig.yaml.
If an expected dynamic table is missing, check whether PyRel used a table because the output cannot be represented as a dynamic table.
If an expected table is missing, check whether PyRel created a view instead, or vice versa.
A deploy is refused because the remote has changes
Someone else or another session deployed since your last pull, so the remote is ahead of you.
If the changes are shared deployment state, run rai models pull to reconcile them, then run rai models deploy again.
If rai models pull reports code-owned changes, those changes live in a collaborator’s source files, so reconcile them through version control with git pull first, then pull and deploy.
Pass --path so PyRel can verify that your source is in sync.
rai models merge reports the branch is behind its parent
The parent advanced after you branched. Switch to the branch, run rai models pull, then run rai models deploy to rebase it onto the parent’s latest state before you try the merge again.
rai models pull won’t overwrite shared_model.py
Pull protects a locally edited shared_model.py from being overwritten. Re-run rai models pull --force. PyRel backs up your current file to shared_model.py.<timestamp>.bak first.
rai models teardown refuses because of children or unmerged changes
A branch with downstream branches or unmerged work is protected. Merge or tear down the child branches first, or pass --allow-children or --allow-unmerged to override that protection. Note that doing so can orphan or discard that work.
rai models branch says the parent isn’t a provisioned model
You can only branch from a deployed model. Deploy the parent schema first with rai models deploy, then branch.