Our blog contains the activity stream of Orchard Dojo: general news, new resources or tutorials are announced here.

Hosting Orchard Core at Exoscale

It's not all about hyperscalers: You can run your Orchard Core app in the cloud even if not using Azure, AWS, or some other giant like these. Let's see how we can do this with Exoscale! Blog post series overview This post is part of our series about hosting Orchard Core at European cloud providers. Orchard Core has built-in support for a lot of Azure services, including Media storage, secret storage, e-mail delivery and sending SMS, AI search, and even observability with Azure Application Insights with the Lombiq module. AWS also has support for Media storage. For cloud-hosted full-text search we can use Elasticsearch, and Redis to aid with horizontal scaling. How does using a smaller cloud provider, without specific support from Orchard Core, compare? We've looked into hosting a vanilla Orchard Core app as a proof of concept, and are sharing our experiences here. The goal was to run Orchard Core with a minimal hosting environment, but with a stateless web server (so, keeping the database and Media storage separate). This could've been simplified to only use a web server (and keep the database in a local SQLite file, as well as use local Media files), but we wanted to go a bit more realistic than that. To be able to play around with the web server from the GUI too, we've chosen Windows as the OS if available. Additionally, we also checked if the platform helps with any of the other common infrastructure requirements, like sending e-mails or observability. Note that while we usually received some credits from the cloud providers for testing, they are not paying for these reviews, and any endorsement or critique is our own. What's Exoscale? Exoscale is a cloud provider from Switzerland, with hosting locations back at home, as well as in Germany, Austria, Bulgaria, and since July also in Croatia. As far as services interesting for Orchard Core hosting go, they offer VMs with load balancing, AWS S3-compatible object storage, and databases as a service. Managed Kubernetes is also available, but for the sake of this post, trying to host a simple app, we won't create a cluster. Instead of Redis, you can create Valkey instances (Valkey is an open-source fork of Redis), which, for now, should just work with Orchard Core's Redis support. While OpenSearch is also available, that's incompatible with Orchard's Elasticsearch integration. DNS management is also available, though that's something I'd keep at Cloudflare for a real-life app. I'd also use Cloudflare as a CDN, despite Exoscale offering a CDN layer (with Akamai) on top of their object storage; but since in Orchard Core we don't serve Media files from storage directly, that wouldn't help anyway. If you want to send e-mails, implement observability, or add search and indexing other than with Lucene on the web server, you're on your own. Creating an Orchard Core hosting environment You surely already guessed, but we'll need three Exoscale services: A Compute Instance, i.e. a VM, for the web server. A DBaaS database; we'll use PostgreSQL. We could also create SQL Server instances, but not only would the minimum 2-core VM be an overkill for our purposes, it's also much more expensive, starting at about EUR 247/month (vs a Postgres DBaaS starting at EUR 42/month). Object Storage for Media. Let's start with the server! I went with the Swiss zone, since there's no place like home, and opted with a 2-CPU plan with 2 GB of RAM. For this site, 1 CPU would also be more than enough (while to cater for Windows, I'm more comfortable with 2 GB of RAM instead of 1), but there's no 1-CPU 2 GB RAM instance type. [image alt="Creating an Exoscale Compute Instance"]BlogPost/other-posts/2025/hosting-orchard-core-at-exoscale/creating-a-compute-instance.webp[/image] You may notice two things here: On the right, there's a warning about having to create Security Groups for ingress rules first. (Somewhat confusingly the ADD button below it actually creates the compute instance, not a Security Group.) To be able to actually connect to the VM, we also need to edit the default Security Group (or perhaps more diligently, create a new one), then open up the ingress port 80 with TCP (for HTTP), as well as add a rule for RDP. As a last step, you'll also need to add 0.0.0.0/0 (i.e. all IPs) under External Sources, allowing anyone to connect to our web server. Those ENABLE buttons for instances Mega and up mean that you need to ask Exoscale support to allow them "after a manual screening process". This I find a bit strange. Until the server started, so in the meantime, I created an Object Storage bucket in the same zone (to minimize latency, these resources should all be in the same zone): [image alt="Creating an Object Storage bucket"]BlogPost/other-posts/2025/hosting-orchard-core-at-exoscale/adding-bucket.webp?2[/image] It seems that unlike with other cloud providers where you create a storage account, then a bucket under it, here everyone shares the same storage account. Thus, bucket names should be globally unique. This doesn't seem appealing at a first glance, but apart from the naming issue, I didn't find any practical problem with it. Somewhat counterintuitively, the storage access and secret keys are API keys. You can create these following the docs; for the sake of simplicity, I created a key for the Owner role, but for a production app, you should follow the docs' recommendations and create a restricted role just to access this bucket. Finally, we also need a single-node Postgres database server: [image alt="Creating a managed database"]BlogPost/other-posts/2025/hosting-orchard-core-at-exoscale/adding-postgresql-dbaas.webp[/image] Note that this runs on its own VM. The IP filter is necessary for the web server to access the DB server; the provided CIDR matches the exact IP of it. This is everything we need, so now we can continue with setting up Orchard Core in this environment. Deploying Orchard Core to the web server You can deploy an ASP.NET Core app, including an Orchard Core-using one, with Web Deploy to IIS, using automated deployment from a GitHub Actions workflow, or just by copying its publish outputs to the server. Here, I opted with the simplest option: I published the web app to a folder from Visual Studio (but running dotnet publish from the CLI would be equally suitable), and copied the resulting files to the server via Remote Desktop. Then I could simply run the web app: [image alt="The Orchard Core app running under Windows Server"]BlogPost/other-posts/2025/hosting-orchard-core-at-exoscale/app-running.webp[/image] The app listens to port 80 because I added an appsettings.Production.json file configuring it: { "Kestrel": { "Endpoints": { "HttpEndpoint": { "Url": "http://*:80" } } } } This allows you to simply open the IP of the server in a browser, or point a DNS A record to it. Our site is now available on the internet! ...after we allowed the exe of our web app through the Windows Firewall, that is. You may notice that this is a really barebones approach: There's no IIS, no HTTPS, no proper deployment. And you're right! This is a focused example of getting Orchard Core running, but we'll return to the missing pieces later. Setting up Orchard Core Running the Orchard Core setup is standard: Just fill out the usual settings on the setup screen, and point the app to the Postgres DB. For the latter, you can copy the Postgres connection string from the Exoscale Console, take the password of the default user from the DB's Users menu, and ask your favorite AI tool to turn them into an Npgsql connection string that .NET uses. Once setup finishes, you can configure Media storage too. I've elaborated above how to set up a bucket; the service URL for it will be in the format https://sos-{zone}.exo.io, with our Switzerland zone being ch-dk-2. Now it's time to configure the connection for the app. Again, you can use the appsettings.Production.json file to add something like below, resulting in the following full file: { "Kestrel": { "Endpoints": { "HttpEndpoint": { "Url": "http://*:80" } } }, "OrchardCore": { "OrchardCore_Media_AmazonS3": { "ServiceURL": "https://sos-ch-dk-2.exo.io", "Credentials": { "SecretKey": "add the secret of the user you created", "AccessKey": "add the access key of the user you created" }, "CreateBucket": true, "RemoveBucket": true, "BucketName": "oc-media" } } } Be sure to change the ServiceURL value to that of your instance, as indicated on its Overview page (under "S3 endpoint"). That's actually an undocumented configuration key that comes very handy when, as we do here, you connect to an S3 account outside of AWS. Now restart the app (so it reads the updated configuration file) and enable the Amazon Media Storage feature from the Orchard Core admin. It'll work, despite it not running in Amazon. Magic! And with this, our app fully works! Why would you (not) use Exoscale? OK, so we've seen that running Orchard Core under Exoscale works. This is not a huge surprise, but it's nice to get it verified and have the basics figured out. Why would we do this, though, instead of choosing the beaten path with Azure or AWS, for example? European Exoscale is not just hosting in Europe, but the company itself is also based in a European country. If that matters for you, either for sentimental reasons or as a matter of company policy (like using a provider that's not under the US CLOUD Act), it can be a big plus. Note though, that while Switzerland is highly aligned with the EU legally, it's not an EU member state, nor part of the European Economic Area. Costs Exoscale can be a lot cheaper. At €17/month, the Exoscale server provides roughly the same as an A1_v2 Azure VM, which costs €45/month. However, the smallest managed Postgres instance at €42/month is way above what we'd use on Azure for an entry-level app, where an S0 Azure SQL DB would be around €13/month. So, in the end, Exoscale costs around EUR 60/month, while the roughly equivalent service in Azure would be basically the same. We do get more with Exoscale (a dedicated VM for the DB, and I think the web server is faster too), a lot more room to grow, but the cost advantage, in itself, disappears, at least in the beginning. For an entry-level app the storage is cents in both cases, but if you increase your usage, Exoscale's storage costs can be dramatically lower (EUR 0,00002750/GB vs Azure's Standard Hot tier at EUR 0,0185/GB). Network costs at Exoscale (EUR 0,02/GB for egress) and per-operation costs at Azure (there's no simple number for this) complicate things, though, so be sure to calculate what's applicable for your app. All this being said, at Azure, we'd rather use App Services: This is a higher-level "websites as a service", offering a bunch of convenience features like automated deployment, staged publishing, and configuration management. The smallest proper App Services-based infrastructure (with a staging slot and separate staging and production DBs) comes at around €90/month, though, but that also contains a lot of features lacking from VMs. Simplicity Without consulting any resource, just by playing with the Console, I could fully understand the Exoscale offering (I think...) within like half an hour. Getting the full Orchard Core environment running wouldn't have been much more, have I not written this post at the same time (plus getting stuck on the mentioned storage configuration). The cons Coming from frequently using the cushy but extremely complex ecosystem of Azure, using a more basic provider like Exoscale is certainly an interesting change. It seems to me that ultimately, there's a lot of work to do if you want to have a full-blown, highly automated hosting environment for Orchard Core, with an option for horizontal scaling. Below are the features that I'd consider necessary, and while these are given with Azure, you need to roll your own solutions with Exoscale: Staged publishing (blue-green deployment): You'd need to set up multiple VMs or multiple sites, and manage swapping staging with production somehow on your own. Automated deployments: I think under a Windows server, the easiest would be to set up Web Deploy and use it from an automation platform like GitHub Actions. Observability: You'd either need to host an observability platform yourself, or use an external service. The lack of a search/indexing service is less of an issue, because you can just use the Elastic Cloud (what's the option in Azure and AWS too), and I'd consider the lack of an e-mail service not a huge issue either (you can just use something like SendGrid). Managed Valkey is good, but starting at €43/month, it's not priced for an easy entry (just as Postgres), although it also provides a lot more resources than the lowest tiers of Azure Cache for Redis do (for a lower price). Yes, you could build an all-in-one infrastructure with Kubernetes, and have containers with services for all the above features (and a DB and storage as well), but that's a large complexity you need to manage. Using Terraform, supported by Exoscale, to ramp up your infrastructure can cut down on manual repetition (and aid portability to other providers to an extent), but is also work that you have to invest. And at the entry level, managed solutions for services like SQL and Redis (instead of hosting your own VMs/containers) can be more cost-efficient too (but that flips if your app's demand increases).There are also three notable things I very much didn't like about Exoscale's pricing: All prices on the pricing page are shown for usage hours only. That's necessary, but a metric frequently a lot more useful is the monthly cost. While I can always multiply by 730 to get an approximation of a month, it would be much clearer to have it indicated in the pricing table already. Also, I'd welcome seeing these hourly and monthly costs indicated in the Exoscale Console, when creating resources, too. The Pricing Calculator helps, and it does show monthly costs too, but it's better to show this information everywhere. The prices in EUR, CHF, and USD are exactly the same. Given that 1 CHF is 1,07 EUR, and 1 EUR is 1,16 USD as of writing this, there's a significant difference in your costs depending on what you pay with (you pay almost 25% more in CHF than USD). According to the website, "Your account can be setup in either CHF, EUR or USD, depending on your initial legal residence." This seems like an arbitrary disadvantage for Swiss users, and an advantage to non-EU ones. The latter is a surprising policy for a company primarily targeting EU customers. I'd welcome the price differences to disappear.You can only pay in advance, at least as a new customer. This requires you to either be ready to top up your balance any time you get a low credit notification, or you need to park an excessive amount at Exoscale. In either case, it's also a manual process. It would be better if they would just charge my card after each month's usage. So, what should I do?Based on the feature set, Exoscale can be a good choice to host your Orchard Core app if you don't need anything beyond what's easily possible with the existing offering, or if you already invested into a portable Kubernetes-based infrastructure. However, I think the particularities of Exoscale's pricing are not for everyone. And if you need help hosting Orchard Core at Exoscale, Azure, or onboard of a satellite (not kidding), drop us a line!

Latest tutorials

Hosting Orchard Core at Exoscale
zoltan.lehoczky
Step away from that Node.js
sara.el-saig

Featured tags

AI
SMS
IIS
MCP
API
SEO
SSL
All tags >

Copilot Integration, Last call: Speaker application for Orchard Harvest 2026 - This week in Orchard (01/05/2026)

This week, Mike Alhayek shows how to use Copilot directly inside Orchard Core!

But before that, check out some code where you can see that, starting now, Orchard supports static data migration methods, and suppressions are no longer required for migration steps that don't use instance state.

Welcome the first contribution from Jack Liu, who made the pagination of the List Part configurable to decide whether to show a full pager with page numbers or just the arrows to navigate to the previous and next pages.

Do you know that since 2013, we've been working with Óbuda University in a hands-on way to teach web development? If you are interested in our Orchard Core courses at the university, check out our post on our site!

As we mentioned, we started publishing last year's Harvest recordings to YouTube. Check them out for some inspiration, and don't forget to apply to be a speaker for this year's Harvest by the 5th of May, midnight, anywhere on Earth!

Ready to explore? Let's dive in!

AI Knowledge Base Indexing, Arguments Source Generation - This week in Orchard (10/04/2026)

This week in Orchard, we've got some exciting updates you won't want to miss!

Mike Alhayek showcases Knowledge Base Indexing, which automatically chunks, embeds, and indexes source documents into a unified AI Knowledge Base index for efficient vector search.

Hisham Bin Ateya introduces the new OrchardCore.ContentFields.Core module. You can now use content fields without pulling in the entire ContentFields module.

Thanks to Georg von Kries, Orchard Core now leverages source generators to boost performance by generating code at compile time and reducing reflection on critical paths.

And don't miss our RESCON case study, highlighting how we supported the builders of Ontario.

Ready to explore? Let's dive in!

Support comments in recipes, multitenant decoupled Blazor InteractiveServer example - This week in Orchard (05/04/2024)

Support comments in recipes, support Create and CreateAsync in DataMigrationManager, and a demo about a Multitenant Decoupled Blazor InteractiveServer Example! Let's see the details! Orchard Core updates Support comments in recipes The recipe reader doesn't support comments in JSON (it's a mode that isn't turned on). This can be annoying when you have a lot of recipes and need to make notes in them as to why you did something. It's because System.Text.Json by default doesn't support comments. We parse using JOptions.Document that is not configured to allow comments. From now on, the settings of the JOptions have been changed to allow comments in recipes. Support Create and CreateAsync in DataMigrationManager You could run into a problem with data migrations: your migrations may not proceed after a module's initialization. The reason for that could be a change of synchronous code to asynchronous. It can happen because you may forget to add the Async suffix in the CreateAsync method. That could be OK, but there is inconsistency in the behavior of UpdateFromX(Async) and Create(Async). Updates work correctly no matter what suffix but Create does not. Now, this behavior has been unified; both Create and CreateAsync methods are now supported. Demos Multitenant Decoupled Blazor InteractiveServer Example In this demo, you can see how to use Orchard Core in a decoupled scenario. There's also a discussion under a GitHub PR to add a Blazor guide for decoupled CMS. Sijmen Koffeman decided to follow this guide and make this a multi-tenant application because that would be very interesting to do in his current situation where he maintains a line of business applications that they provide to several tenants. He took the example from the guide and made it further, and that works so far. In his demo, you can do a deep dive into the code that sits inside the Orchard Core Multitenant Decoupled Blazor InteractiveServer Example repository to see the current state of this work. Head to YouTube for a recording to know more! News from the community Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 472 subscribers! We have started this newsletter to inform the community around Orchard of the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post is published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news about Orchard and the details of the topics above, don't forget to check out the recording of this Orchard meeting!

How to migrate an Orchard 1 application to Orchard Core

How long have you been using Orchard 1 for your website? How satisfied are you with its features and performance? Have you encountered any limitations or challenges with Orchard 1 that you hope to address? If you are looking for a modern and improved version of Orchard 1, you might want to consider migrating to Orchard Core which is a better version of Orchard 1 in many aspects. Orchard Core is not just a port of Orchard 1, but a new and improved system that offers many benefits over its predecessor. What are the benefits of migrating to Orchard Core? You can enjoy numerous remarkable benefits by migrating to Orchard Core. First of all: Performance. As the saying goes: “The faster the better.” Orchard Core is so fast that an output cache module like for Orchard 1 is not required. For example, you can set up a site with the blog recipe in less than half the time that Orchard 1 needs for the same task. It is portable: it can run on Windows, Linux, and macOS, as well as on Docker containers. It has a more flexible and extensible modular framework that allows building modular and multi-tenant applications more easily. Orchard Core also supports NuGet packages for modules and themes, thus creating a new website with it is actually as simple as referencing a single meta package from the NuGet gallery. It is built on ASP.NET Core. This means that it can use any C# language version without any limitations, while Orchard 1 uses .NET Framework 4.8 which means it’s restricted to C# 7.3. It has new features that are not available in Orchard 1, such as GraphQL API, OpenID Connect, Liquid templates support, and more. It supports all major site building strategies: Full CMS, Decupled CMS, Headless CMS. This is just the tip of the iceberg, to read more about Orchard Core check out its documentation. Preparations The first thing to know is that there is no easy or fully automated way to migrate your website and content. The two systems are different in terms of data schema, modules, themes, and features. However, in general, we can say, that proficiency in Orchard 1 puts you in a good position to develop in Orchard Core too. There are a lot of similarities for example menu points on the admin UI, or how migrations work, etc. A good first step would be to take a look at your current application. Do you want to replicate what you have there, or do you also want to improve it? Migration is a good opportunity to renew your site, change design, and functionality, get rid of obsolete elements on the site, etc. You'll also need to know Orchard Core before attempting a migration. If you are new to Orchard Core development, we recommend that you start with our Dojo Course 3 - the full Orchard Core tutorial. This course covers the fundamentals of Orchard Core for both users and developers. New CMS, familiar features Before starting the migration, it is recommended to check your most used, most important features in Orchard 1 and list them. There is a good chance that there is an equivalent feature in Orchard Core, but it changed and has been upgraded, so you will need to do some research. For example, if you want to migrate your custom form, instead of Orchard.DynamicForms you can use OrchardCore.Forms to achieve the same result. Or instead of Orchard.Taxonomies you can use OrchardCore.Taxonomies. The Workflows and Audit Trail modules were ported to Orchard Core and improved too. Migrating content types Content types are composed of content parts and fields, which provide different functionalities and data types for your content. If you want to migrate your content types, you will need to recreate them in the new system. Luckily, as with features most of Orchard 1 content parts and fields have their equivalent in Orchard Core. Some examples are BooleanField, NumericField, TitlePart, or CommonPart. In this case, you can recreate your content type from the admin UI, from a recipe, or with a migration. Let’s say you have a BlogPost content type in Orchard 1, which has the following parts and fields: TitlePart: Provides a title for the blog post. AutoroutePart: Provides a URL for the blog post. BodyPart: Provides a rich text editor for the blog post content. MediaLibraryPickerField: Provides a way to select an index image for the blog post. Now take a look at what we have in Orchard Core. We have TitlePart and AutoroutePart in Orchard Core too, so that is handy, but there is no BodyPart and MediaLibraryPickerField. But after some research, we can find the equivalent of them: HtmlBodyPart and MediaField. We have all the parts and fields in the new CMS to recreate the BlogPost content type. In this example, we are creating it with the help of a migration file BlogPostMigration.cs: using OrchardCore.Autoroute.Models; using OrchardCore.ContentManagement.Metadata; using OrchardCore.ContentManagement.Metadata.Settings; using OrchardCore.Data.Migration; using OrchardCore.Html.Models; using OrchardCore.Media.Settings; using OrchardCore.Title.Models; namespace MyProject.Migrations; public class BlogPostMigration : DataMigration { private readonly IContentDefinitionManager _contentDefinitionManager; public BlogPostMigration(IContentDefinitionManager contentDefinitionManager) => _contentDefinitionManager = contentDefinitionManager; public int Create() { // Define a part called BlogPost with some fields. _contentDefinitionManager.AlterPartDefinition("BlogPost", part => part .WithField("Image", field => field .WithDisplayName("Index Image") .WithPosition("0") .WithSettings(new MediaFieldSettings { Multiple = false, })) ); // Define a type called BlogPost with some parts. _contentDefinitionManager.AlterTypeDefinition("BlogPost", type => type .DisplayedAs("Blog Post") .Creatable() .Listable() .Draftable() .Versionable() .Securable() // Add the Title part to provide a title for the blog post. .WithPart(nameof(TitlePart), part => part .WithPosition("0")) // Add the BlogPost part to provide some fields for the blog post. .WithPart("BlogPost", part => part .WithPosition("1")) // Add Autoroute part and configure it to use a pattern based on the title. .WithPart(nameof(AutoroutePart), part => part .WithPosition("2") .WithSettings(new AutoroutePartSettings { Pattern = "{{ ContentItem | display_text | slugify }}", })) // Add the HTMLBody part to provide a rich text editor for the blog post content. .WithPart(nameof(HtmlBodyPart), part => part .WithPosition("3") .WithEditor("Wysiwyg")) ); return 1; } } …and don’t forget to add your migration to the Startup.cs file to register it. However, if you have a content type with custom fields and parts, with custom functionality, and features, you will also have to recreate those fields and parts by reimplementing your custom code in the new project. Migrating content items The best way to add numerous content items is with a recipe. To migrate the content items, first, make sure that you have the content type created in the new system. After that, you will need to export your content items. The file formats of the recipes are different in the new CMS: Orchard 1 uses XML, while Orchard Core uses JSON, however, their functionality and use cases are similar. You will need to create an Orchard Core recipe: This can be done either manually, or Lombiq has a feature for this in the open-source Helpful Extensions module called Lombiq Helpful Extensions - Orchard 1 Recipe Migration. It has built-in functionality for content items with the most used content parts, but you can extend the built-in functionality to support more parts. Conclusion Migrating from Orchard 1 to Orchard Core is a challenging but rewarding process that can bring many benefits to your site. However, it requires careful planning, preparation, and testing to ensure a smooth transition. We have extensive experience and expertise in migrating sites from Orchard 1 to Orchard Core, and we can help you migrate your project. Lombiq has recently renewed and migrated its main site http://lombiq.com to Orchard Core too, and we are very happy with the results. You can check out our site and see how Orchard Core can power your site too. Happy migrating!