Platform
Learn
Developer docs User guide Quickstart Blog
Company
Services About Contact Links Get started

Upgrading from 1.5 to 2.0

Updated

Raytha 2.0 upgrades a 1.5 database in place. The upgrade changes the schema and also rewrites some of your data, and the data changes cannot be undone by a migration rollback. Take a backup first; restoring it is the way back.

Before you start

  1. Be on PostgreSQL and on 1.5. The schemas of 1.5.0, 1.5.1 and 1.5.2 are identical, so any 1.5.x database upgrades the same way. From 1.4.x, bring the database to 1.5.0 first with db/Postgres/v1_4_0_to_v1_4_1.sql and db/Postgres/v1_4_1_to_v1_5_0.sql from the repository.
  2. SQL Server has no upgrade path. 2.0 supports only PostgreSQL. Move your data to PostgreSQL while still on 1.5.0, then upgrade.
  3. Back up the database and the file storage. See Backups and restores. For a Docker setup:
    docker compose exec -T db pg_dump -U postgres -Fc raytha > raytha-1.5-before-2.0.dump
  4. Check the Website URL under Settings, Configuration. 2.0 builds media links and email links from it. A stale value, such as an old domain, breaks images in API responses and links in emails.

Run the upgrade

Choose one. Both end at the same schema.

Option A: let Raytha migrate

Start 2.0 with APPLY_PENDING_MIGRATIONS=true. The setting is on in appsettings.json and in the Compose examples in these docs. Raytha applies the single 2.0 migration, 20260921002825_v2_0_0, then starts. With the published image, change the tag to raythahq/raytha:2.0.0 in your Compose file and pull:

docker compose pull app
docker compose up -d app
docker compose logs -f app

If you build from the repository instead, git pull and run docker compose --env-file .env up -d --build.

Option B: apply the SQL script yourself

Use this when you want to see the output, or when the app does not have permission to change the schema. Stop the 1.5 app, run the script, then start 2.0 with APPLY_PENDING_MIGRATIONS=false.

psql -v ON_ERROR_STOP=1 -h localhost -U postgres -d raytha \
  -f db/Postgres/v1_5_0_to_v2_0_0.sql

Against the Compose database:

docker compose exec -T db psql -v ON_ERROR_STOP=1 -U postgres -d raytha \
  < db/Postgres/v1_5_0_to_v2_0_0.sql

The script runs in one transaction. If any statement fails, nothing is applied. On success its last statement records the migration in __EFMigrationsHistory, so a later start with APPLY_PENDING_MIGRATIONS=true has nothing left to do. Read the output in psql: it is the only place the date notices described below appear.

Schema changes

  • New tables: EmailLogs, Webhooks, WebhookDeliveries, UserWebTemplate.
  • OrganizationSettings gains AuditLogRetentionDays, BackgroundTaskRetentionDays, EmailLogRetentionDays and WebhookDeliveryRetentionDays, each defaulting to 180.
  • WidgetTemplates and WidgetTemplateRevisions gain a fields definition (_FieldsJson), and ContentTypeFields gains sub-field definitions.
  • Routes and RaythaFunctions are linked to each other, so a function can serve an HTTP route.
  • AuditLogs gains ImpersonatorEmail.
  • New indexes.

Data rewrites

These happen during the upgrade, whichever option you use.

What changesDetails
Date fields become ISO dates1.x stored dates as m/d/yyyy or in the server's culture. Each value is rewritten to YYYY-MM-DD, or YYYY-MM-DDTHH:MM:SS if it had a time. This covers published content, drafts, revisions and the trash.
Media links become root-relativeLinks such as http://localhost:5200/raytha/media-items/objectkey/abc_x.png in rich text and page-builder widgets lose their scheme and host, but only when the object key exists in this database's media library. Covers content, drafts, revisions, trash, site pages and site page revisions. Links to other sites and all templates are left alone.
Manage Media is a separate permissionAny role that has Manage System Settings, Manage Content Types, or Edit on any content type is granted Manage Media, so nobody loses access to the media library. Other roles do not get it.
Magic-link sign-in uses a one-time codeThe email template raytha_email_login_beginloginwithmagiclink and the page template raytha_html_login_magiclinksent are replaced with code-based versions, unless they already use the code. Your previous content is saved as a revision first. Magic-link emails sent before the upgrade stop working.
Built-in widget templates get field definitionsThe templates hero, wysiwyg, imagetext, card, faq, cta, embed and contentlist (and their revisions) get their field definitions for the 2.0 page builder. Only templates with an empty field list are touched; your own widget templates are not.

How dates are converted

For each date field, the script infers the day/month order from that field's own values: a first number above 12 means day-first. Then it rewrites every value in the field.

Stored in 1.5After upgradeWhy
3/15/20242024-03-1515 cannot be a month, so month-first.
1/2/2024 3:45:00 PM2024-01-02T15:45:00Month-first in this field; the time is kept.
31/12/2023 and 4/5/2024 in the same field2023-12-31 and 2024-05-04The 31 makes the field day-first, which settles the ambiguous value.
not a dateunchangedUnrecognised values are left as they are and reported.

A value that could be read either way, with nothing else in its field to decide, is also left unchanged. Each such field produces a notice such as Date values left unchanged in events.starts: unrecognized format (1 values). Only psql shows it. Option A hides it, so run this query after any upgrade to list what was left behind:

SELECT t."DeveloperName" AS content_type, f."DeveloperName" AS field, ci."Id",
       ci."_PublishedContent" ->> f."DeveloperName" AS value
FROM "ContentItems" ci
JOIN "ContentTypes" t ON t."Id" = ci."ContentTypeId"
JOIN "ContentTypeFields" f ON f."ContentTypeId" = ci."ContentTypeId" AND f."FieldType" = 'date'
WHERE coalesce(ci."_PublishedContent" ->> f."DeveloperName", '') !~ '^([0-9]{4}-[0-9]{2}-[0-9]{2}.*)?$';

2.0 reads a leftover value in the server's culture, as 1.x did. A value it cannot parse reads as an empty date, and the admin editor shows the stored text and asks you to enter the date it meant.

Check the result

  1. Open /healthz/ready. Both checks should be Healthy. See Health checks.
  2. Sign in at /raytha. The admin is now a React app that replaces the Razor admin.
  3. Run the leftover-dates query above and fix any rows in the admin.
  4. Open a few public pages that show images or dates, and look at the HTML. Root-relative media links resolve against the site, so a wrong Website URL shows up here.
  5. Send yourself a sign-in code or a password recovery email. See Sending emails.
  6. If you use the REST API, check a response with an image. API responses turn root-relative media links back into absolute ones using the Website URL, so existing consumers still get full URLs.

Roll back

Restore the backup you took before the upgrade, and run the 1.5 version of the app against it. Running the 1.5 app against an upgraded database is not supported: it cannot read the converted dates, and the migration cannot be reversed because the data rewrites are one-way.

docker compose stop app
docker compose exec -T db dropdb -U postgres raytha
docker compose exec -T db createdb -U postgres raytha
docker compose exec -T db pg_restore -U postgres -d raytha --no-owner < raytha-1.5-before-2.0.dump

Restore the file storage backup as well if media was uploaded after the upgrade and you want the two to match.

Gotchas

  • Webhooks, the email log and the new retention settings start empty. Nothing is migrated into them.
  • The script only rewrites media links whose object key is in this database's MediaItems. A link to an uploaded file whose media row was deleted keeps its absolute URL.
  • Customised copies of the built-in magic-link templates are replaced too, unless they already contain Target.Code or magic-link/complete. Look in the template's revisions to merge your edits back.
  • Running Option A on a database that already had the script applied does nothing, because the migration is recorded.
  • Do not edit __EFMigrationsHistory by hand to retry an upgrade. Restore the backup and start again.

Next steps