You can make a couple of changes to Umbraco to make it a bit more efficient. Use these tips to your advantage, to make Umbraco perform better, but some tips may require assistance of your Windows Server IIS web server administrator. Remember him next System Administrator Appreciation Day 🙂
Please not that I’m not an ASP.NET programmer.
Key for a better performing Umbraco website is less writing to disk, or reading from disk. I find that a bottleneck, even with SSD’s. Less reading and writing also saves on RAM (memory) and CPU usage, and thus process execution time in IIS.
In no particular order I’ll address the following topics:
- ASP.NET Compilation tempDirectory
- File Change Notification (FCN)
- Umbraco ModelsBuilder ModelsMode
- HTTP Expires headers
- ImageProcessor cache folders,
- AllowSubDirConfig, SqlCe database file to SQL Server
- SQL Server sessionState
Some changes might degrade performance, especially when done wrong. When in doubt, consult your system administrator first. These settings below work in my environment, maybe not yours.
All to often in a shared hosting environment, you may share your ASP.NET compilation temporary directory with other users. This may not be particularly a bad thing, but when a lot of IIS worker processes are reading and writing in one directory, you may find your website waits for other disk operations. And that takes time.
If ASP.NET 4 runs in a Full trust configuration, you can overwrite ASP.NET’s compilation tempDirectory setting to a folder in your website. For example, if the physical path to the content for the virtual directory of your website is
z:\shared\sites\example.com\www, and the
msIIS-FTPRoot is configured as
z:\shared\sites\example.com, you can create an extra folder
temp in the FTPRoot and configure that as your compilation tempDirectory.
Locate Umbraco’s compilation node in
<system.web>, and add the tempDirectory:
<compilation tempDirectory="z:\shared\sites\example.com\temp" />
Make sure this folder is writable for the user your website runs as.
Change the file change notification in ASP.NET, so less resources are used for keeping track of changed files. Especially in large media folders FCN can degrade performance. The All about ASP.Net File Change Notification (FCN) post on Shazwazza’s blog has more insights about this.
httpRuntime key in your web.config and add
fcnMode="Disabled"to completely disable File Change Notification for that folder, or set it to Single. Single is the default since Umbraco 7.5.3, see U4-7712.
Indicates the number of dynamic recompiles of resources that can occur before the application restarts. This attribute is supported at the global and application configuration levels, but not at the directory configuration level.
ASP.NET increases the NumRecompilesBeforeAppRestart property value every time an assembly is invalidated and fails to be deleted.
By default, IIS restarts the Umbraco web application after 15 dynamic recompiles of resources. When you have a large Umbraco website, you may experience a long time before the compilation process of your scripts is finished. During the compilation, CPU usage spikes and your sites become unresponsive.
You can decrease the number of application restarts by increasing the NumRecompilesBeforeAppRestart property in your web.config file. Umbraco has this value set to 50, you can increase this to 150, 200, or whatever:
<compilation defaultLanguage="c#" debug="false" batch="false" targetFramework="4.5" numRecompilesBeforeAppRestart="250">
Every time you publish a page in Umbraco, the
~/App_Data/umbraco.config file is saved as cache. In ASP.NET’s mind this is a configuration change, and the application is restarted by IIS with reason ConfigurationChanged.
This behavior is easily disabled in
- add the following two (2) lines to
- delete the
This is addressed in U4-7101. I had to fiddle a bit with where to put these lines, and after some testing, lines 38/39 seemed fine.
The thing I could not test, unfortunately: Umbraco ModelsBuilder ModelsMode.
Umbraco ships with a NuGet package called ModelsBuilder. This generates strongly typed models that can be used in Umbraco views. The default behaviour is to generate the models on the fly, in memory, at runtime. This has the advantage of the models always being up to date whenever a content type is modified.[…]
We changed the setting to Dll mode, which compiles the models to a Dll that is then copied into the bin folder. This fixed the 10s slowdowns, and also fixed the Max Recompilations Reached errors we were seeing above. The downside with this is that it requires a manual step after a deploy to build the models from within the back office.
Expires headers? Yes, expires headers. Apparently, they’re not set everywhere for your static Umbraco resources.
By configuring expires headers you tell the browser to keep a local copy of the resource in its cache. This speeds up a next visit or page view. The following example, that you add to your
web.config tells the browser to save it for 14 days, change the duration if you’d like. First determine which folders you have in your Umbraco installation, and then select the appropriate lines to add.
<locationpath="Umbraco_Client"> <system.webServer> <staticContent> <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="14.00:00:00" /> </staticContent> </system.webServer> </location> <locationpath="Umbraco_Client/assets"> <system.webServer> <staticContent> <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="14.00:00:00" /> </staticContent> </system.webServer> </location> <locationpath="Umbraco_Client/Js"> <system.webServer> <staticContent> <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="14.00:00:00" /> </staticContent> </system.webServer> </location> <locationpath="css"> <system.webServer> <staticContent> <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="14.00:00:00" /> </staticContent> </system.webServer> </location> <locationpath="Media"> <system.webServer> <staticContent> <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="14.00:00:00" /> </staticContent> </system.webServer> </location> <locationpath="scripts"> <system.webServer> <staticContent> <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="14.00:00:00" /> </staticContent> </system.webServer> </location>
There already is a line
<location path="umbraco"> in your web.config, add the three lines there as well.
Keep in mind these folders might be different for different versions of Umbraco.
Want to learn more about HTTP headers? For example, learn how to remove IIS Server version header.
If you don’t use the logfiles Umbraco creates through log4net, then log as less as possible. This limits the number of writes to your disks. In the file
~/Config/log4net.config change the value “true” in “false” for the key
<appendToFile value="true" /> and WARN in ERROR for
<level value="WARN" />.
Umbraco Examine is an extensible indexer and search engine. Add
useTempStorage="Sync" to the nodes as described in U4-5993 and https://our.umbraco.com/forum/umbraco-7/using-umbraco-7/72801-fixing-poor-performance-on-azure.
Specifying “Sync” will mean that the index that is stored in
~/App_Data/TEMP/ExamineIndexes/[IndexName] will get restored to the ASP.NET process’s local temp folder (i.e.
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\vs\7a807fb2\7d5e4942\App_Data\TEMP\ExamineIndexes[IndexName], or
z:\shared\sites\example.com\temp if you’ve changed ASP.NET’s Compilation tempDirectory mentioned above).
Umbraco’s ImageProcessor creates a lot of cache folders for uploaded images and media by default. To decrease the number of reads writes, and to decrease the number of directories the ASP.NET process has to go through, it’s advised to limit the cache folders created.
in ~/Config/imageprocessor/cache.config (DiskCache key). For example folderDepth=”2″ to create a two level deep cache directory structure. The complete line becomes:
<cache name="DiskCache" type="ImageProcessor.Web.Caching.DiskCache, ImageProcessor.Web" maxDays="365" browserMaxDays="7" folderDepth="2" trimCache="false">
This last one is tricky: AllowSubDirConfig controls whether or not IIS searches for a web.config file in every subdirectory. If you have a lot of directories with a lot subdirectory’s (for example the imageprocessor cache mentioned above), the ASP.NET process goes through every directory looking for web.config configuration settings. This takes a lot of time (relatively).
It is enabled by default. System administrators may choose to disable this setting using appcmd:
appcmd set vdir "/vdir.name:"example.com/ /allowSubDirConfig:false /commit
If you want to be able to use Umbraco in a load-balancing hosting environment, you must have an SQL Server database for your site, and not the default SqlCe SDF database file. That doesn’t scale.
There is a how to migrate SDF database to Sql Server post on Nicola Ayan’s blog. I haven’t tested it myself yet, but I plan to.
Having your SqlCe database file converted to SQL Server has the great advantage you now can save your sessions in that database too! No more InProc sessions that are lost after application restarts (application pool recycles), but Out-of-Process sessions, properly saved in an SQL Server database or table (the latter in my case).
I have a how to configure SQLServer sessionState for Umbraco post ready for you.
Out of the box, an Umbraco website runs pretty great. But the default configuration makes it read from and write to disk a lot. Too much if you’d ask me. That is unnecessary, particularly if you don’t use some of the information provided, the log4net logs for example. This post gave you some pointers to optimize Umbraco’s performance, especially if you host dozens or hundreds Umbraco sites on a single server or web-farm.
Let me know if this post helped you in optimizing your Umbraco site! But be careful though, different optimization tips may be valid for different Umbraco versions. Always test first before putting your changes it into production.
Did you find this post interesting? Did it help you solve a problem? If I’ve helped you out and you want to thank me, then why not buy me a coffee?
A small donation of only $5 helps out a lot in the development, research and hosting of this blog.
Thank you for your support.