Skip to content

This feature is currently in Preview.

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.

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.

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.

  1. 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-name with a starter model and raiconfig.yaml. The generated raiconfig.yaml demonstrates a best practice: use separate dev and prod profiles in raiconfig.yaml. By switching profiles, you can keep using the same rai models deploy flow, whether you are developing against an isolated schema with test data, or releasing to a production schema widely used throughout your organization.

  2. Configure your development connection in raiconfig.yaml

    In the dev profile, 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: sf
    connections:
    sf:
    type: snowflake
    account: my_org-my_account
    warehouse: my_warehouse
    rai_app_name: relationalai # name of the RAI app in your Snowflake account
    authenticator: externalbrowser # SSO via browser; for PAT or JWT see README.md
    user: you@example.com
  3. Set the target schema and refresh schedule in raiconfig.yaml

    Again for the dev profile, fill in the model and deployment sections. The schema should 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 script
    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
    schedules:
    standard:
    interval_s: 0 # run on deploy, but do not refresh on a fixed interval
    outputs:
    schedule: standard # assign all outputs to the `standard` schedule
  4. 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.py

    If you have some source tables already, just skip this step and update model.py to use those.

  5. 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 the dev profile and run:

    Terminal window
    RAI_PROFILE=dev rai models deploy --wait

    For command details and options, see rai models deploy. --wait makes the deploy command wait for the initial refresh of the model to finish.

  6. 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 Connection and Station concepts from the starter model.

    You can also query a deployed model through PyRel directly. For the starter model, run:

    Terminal window
    python queries.py

    This 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.

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 Model and 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.

  1. Specify the entry path for your model

    Make sure that your model section specifies where your model can be found. This is done via the path field, which can point to a Python file or module.

    model:
    path: model.py
  2. Set the target schema in raiconfig.yaml

    Add a deployment section 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 development

    This 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.

  3. Define a refresh schedule

    Every model output must be assigned to a schedule. Extend your deployment section 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_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 default
  4. Deploy from the CLI

    Select the right profile and trigger the deployment.

    Terminal window
    RAI_PROFILE=dev rai models deploy

    For 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 default

interval_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_pipeline

For 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: standard

Simple 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.

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 type

Override 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.

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:

Terminal window
RAI_PROFILE=dev rai models deploy

See 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.

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.

  1. 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 for deployment.schema.

  2. 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.

  3. Check the model’s refresh status

    The meta schema exposes a REFRESH_STATUS procedure that returns one row per refresh schedule, showing each schedule’s current status. REFRESH_STATUS is 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 status column can return:

    StatusMeaning
    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 error column contains the failure message.

If any of these steps fails or Snowflake does not show the expected objects, continue to the troubleshooting section below.

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:

SsourceAoutputBoutputCoutput

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.

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.

  • 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.

  • You’re comfortable with rai models deploy and with the deployment.schema setting in raiconfig.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:

Terminal window
rai models list
rai models switch MY_DATABASE.MY_DEV_SCHEMA

rai 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.

  • 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.

  1. Pick the model to branch from

    Confirm your current model, switching if needed:

    Terminal window
    rai models switch MY_DATABASE.MY_DEV_SCHEMA
  2. Create the branch

    Terminal window
    rai models branch EXPERIMENT_X

    For command details and options, see rai models branch.

  3. Switch into the branch and work in it

    Terminal window
    rai models switch EXPERIMENT_X

    Edit your model and deploy as usual:

    Terminal window
    rai models deploy

    Changes land in the branch’s schema. The parent is untouched.

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 plain rai models pull does not bring in the parent’s later changes. You incorporate them explicitly, when you choose to, with rai 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.

  • Op log recording is enabled for the shared model. It is on by default, and rai models pull and rai models deploy use 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 on deploy and pull. rai models pull keeps 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:

  1. Pull before you start

    Terminal window
    rai models pull

    For command details and options, see rai models pull.

  2. Make your changes and deploy

    Terminal window
    rai models deploy

    If a collaborator deployed since your last pull, the deploy is refused with a message telling you to pull first. Run rai models pull and 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.

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.

  1. Switch to the branch

    Terminal window
    rai models switch EXPERIMENT_X
  2. Catch up on the parent and rebase

    Terminal window
    rai models pull
    rai models deploy
  3. Merge

    Terminal window
    rai models merge

    For 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:

Terminal window
rai models merge --delete

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.

Terminal window
rai models teardown # dry run: shows what would be dropped
rai models teardown --force # actually drop it

After 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-unmerged to discard them.
  • It refuses to orphan downstream branches. Override with --allow-children to 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.

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:

Terminal window
rai doctor

If the issue still looks configuration-related, also run:

Terminal window
rai config explain

When 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:

Terminal window
rai doctor report

This 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.