Disclaimer: This website requires Please enable JavaScript in your browser settings for the best experience.

HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideLegal TermsGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Deploy to Windows servers

Describes how to deploy Optimizely Content Management System (CMS) sites through Visual Studio.

By copying application files, you can install Optimizely Content Management System (CMS) through XCOPY deployment. An XCOPY-style file transfer simplifies the deployment and maintenance of Optimizely sites because it does not create registry entries and does not register shared.

The XCOPY architecture in CMS does not store machine—or site-specific information in configuration files so that you can use a shared folder for multiple servers. When you develop in a shared environment with source control, you can keep configuration files checked in and still create a site that you can duplicate in a multi-site deployment.

You can have one single IIS application with multiple sites or multiple IIS applications pointing to the same physical directory. The machine to which you install additional sites does not need CMS installed.

Deploy a site

The following procedure shows how to deploy a CMS site from development to production. You can automate this using MSDeploy or other tools.

You need three artifacts to bring over to the production server:

  • Site (the Optimizely platform and templates with dependencies and modules). These are normally the root of a Visual Studio project.
  • SQL Server database (stored content and so on).
  • Application Data (media BLOBs, search index). These are files normally stored in App_Data for a Visual Studio project. (Versions before CMS 7.6 store these files parallel to the Visual Studio project by default.)

📘

Note

The following steps assume you have an existing site and good knowledge of using Visual Studio, Internet Information Services Manager and SQL Server Management Studio.

1. Create the deployment artifacts

Create the deployment artifacts on the development machine to publish a package you can install on the target server.

  1. Open a Visual Studio project for the site you are deploying.
  2. Establish NuGet dependencies for the Optimizely products used. (Default for CMS 7.6 and later.)
  3. Include files in modules, modulesbin and App_Data folders in the project. (You also can copy those folders manually to the output folder.)
  4. If you installed Optimizely Search, include the IndexingService folder into the project.
  5. Right-click the project in Visual Studio and click Publish. Specify a publish destination of type FileSystem.
  6. Under Settings, ensure Exclude files from the App_Data folder is not checked to restore those files on the destination. (Do not select Precompile during publishing because it turns off required functionality such as Virtual Path Providers.)
  7. Publish the project.

📘

Note

If the database files are not stored in App_Data, see Back Up and Restore of SQL Server Databases on MSDN for details about moving databases.

2. Deploy the artifacts on the target server

📘

Note

The following steps are performed on the target server and assumes you have moved the artifacts created in the previous section to the new server. The web server should have IIS and ASP.NET installed. You also need SQL Server Management Studio to connect to the database server. To use Optimizely Search, you also need the Windows feature HTTP Activation (Roles and Features > .NET Framework 4/4.5 Features > WCF Services) installed on the web servers.

  1. Attach the database. See Attach a database on MSDN.

    To complete this step, move the database files from the App_Data folder to the designated data folder for Microsoft SQL Server.

    You can also use backup or restore to attach a database. See Back up and restore of SQL server databases on MSDN.

  2. Create an SQL Login:
    a. Right-click on the Security node and select New > SQL Login.
    b. Select SQL Server Authentication.
    c. Unselect User must change password at next login.
    d. Under User Mapping, select the database and ensure the user is a member of the db_owner group.

  3. On the web server, create folder for your site (for example c:\inetpub\Site1), and copy the contents of the deployed site.

  4. Set access rights on the App_Data folder so that the group IIS_IUSRS have Change access.

  5. Open Internet Information Services Manager and connect to the web server.

  6. Create an IIS site:
    a. Right-click the Sites node.
    b. Select Add Website.
    c. Select the wwwroot subfolder as the Physical path for the site.

  7. Update the EPiServerDB connection string in the configuration file in the site root (such as web.config or connectionStrings.config, depending on setup) with connection details to the database (server, database, SQL login, and password).
    Make sure your db connection string contains MultipleActiveResultsSet=true. It is enabled by default when you install a website from scratch but not if you generate a connection string from the server explorer in Visual Studio.

  8. If you installed Optimizely Search, update the episerver.search section to make sure the baseUri refers to the IIS site.

📘

Note

For security and performance reasons always move media BLOBs to a folder outside the application root (as described in the next section), either on a fileshare or a local folder. Storing BLOBs in the App_Data folder is only designed for development purposes.

3. Add load-balancing support

This section describes how to enable load-balancing, configure remote events, and store BLOBs externally (files on a file share). The example uses the default provider for events (WCF over UDP) and the default provider for storage of BLOBs.

📘

Note

UDP broadcast is not supported by Azure virtual networks. Use the dedicated Azure Service Bus provider instead or Configure events over WCF over TCP.

  1. Move the Blob subfolder of the App_Data folder to a shared server, such as the database server, and then share the folder. Give access rights for the share and the folder to the user running the Application Pool for the site in Windows.
  2. Edit EPiServerFramework.config and add configuration for the file BLOB providers. Make sure the path attribute is valid:
    <blob defaultProvider="fileShare">
      <providers>
        <add name="fileShare" path="\\dbserver\blobs" type="EPiServer.Framework.Blobs.FileBlobProvider, EPiServer.Framework" />
      </providers>
    </blob>
    
  3. Clear the browser cache and test the setup by browsing the site. The images on the site should work as before but from the shared location.
  4. Enable the default provider for remote events by configuring the endpoints RemoteEventServiceClientEndPoint and RemoteEventServiceEndPoint in web.config, which automatically enables the default provider.
    Code example for CMS 11.1 and higher:
    <system.serviceModel>
    	<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
    	<extensions>
    		<bindingElementExtensions>
    			<add name="udpTransportCustom" 
               type="Microsoft.ServiceModel.Samples.UdpTransportElement, EPiServer.Framework.AspNet"/>
    		</bindingElementExtensions>
    	</extensions>
    	<services>
    		<service name="EPiServer.Events.Remote.EventReplication">
    			<endpoint name="RemoteEventServiceEndPoint"
                    contract="EPiServer.Events.ServiceModel.IEventReplication"
                    binding="customBinding"
                    bindingConfiguration="RemoteEventsBinding"
                    address="soap.udp://239.255.255.19:5000/RemoteEventService" />
    		</service>
    	</services>
    	<client>
    		<endpoint name="RemoteEventServiceClientEndPoint"
                  address="soap.udp://239.255.255.19:5000/RemoteEventService"
                  binding="customBinding"
                  bindingConfiguration="RemoteEventsBinding"
                  contract="EPiServer.Events.ServiceModel.IEventReplication" />
    	</client>
    	<behaviors>
    		<serviceBehaviors>
    			<behavior name="DebugServiceBehaviour">
    				<serviceDebug includeExceptionDetailInFaults="true"/>
    			</behavior>
    		</serviceBehaviors>
    	</behaviors>
    	<bindings>
    		<customBinding>
    			<binding name="RemoteEventsBinding">
    				<binaryMessageEncoding />
    				<udpTransportCustom multicast="True"/>
    			</binding>
    		</customBinding>
    	</bindings>
    </system.serviceModel>
    
    Code example for CMS 10.x
    <system.serviceModel>
    	<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    	<extensions>
    		<bindingElementExtensions>
    			<add name="udpTransportCustom" 
               type="Microsoft.ServiceModel.Samples.UdpTransportElement, EPiServer.Events"/>
    		</bindingElementExtensions>
    	</extensions>
    	<services>
    		<service name="EPiServer.Events.Remote.EventReplication" >
    			<endpoint name="RemoteEventServiceEndPoint"
                    contract="EPiServer.Events.ServiceModel.IEventReplication"
                    binding="customBinding"
                    bindingConfiguration="RemoteEventsBinding"
                    address="soap.udp://239.255.255.19:5000/RemoteEventService" />
    		</service>
    	</services>
    	<client>
    		<endpoint name="RemoteEventServiceClientEndPoint"
                  address="soap.udp://239.255.255.19:5000/RemoteEventService"
                  binding="customBinding"
                  bindingConfiguration="RemoteEventsBinding"
                  contract="EPiServer.Events.ServiceModel.IEventReplication" />
    	</client>
    	<behaviors>
    		<serviceBehaviors>
    			<behavior name="DebugServiceBehaviour">
    				<serviceDebug includeExceptionDetailInFaults="true"/>
    			</behavior>
    		</serviceBehaviors>
    	</behaviors>
    	<bindings>
    		<customBinding>
    			<binding name="RemoteEventsBinding">
    				<binaryMessageEncoding />
    				<udpTransportCustom multicast="true"/>
    			</binding>
    		</customBinding>
    	</bindings>
    </system.serviceModel>
    
  5. If Optimizely Search & Navigation is installed, you cannot load-balanced the search index. You must store it on a primary server. Change the service to allow remote callers by setting an access key under the clients list in the episerver.search.indexingservice section in web.config:
    <add name="ACCESSKEY_HERE" 
         description="local" 
         allowLocal="false" 
         ipAddress="0.0.0.0/0" 
         ip6Address="::/0" 
         readonly="false" />
    
  6. Change the client to access one of the servers with the specific access key by changing the services list episerver.search section in web.config:
    <add name="serviceName" 
         baseUri="http://SERVER1/IndexingService/IndexingService.svc" 
         accessKey="ACCESSKEY_HERE" />
    
  7. The site is ready to be load-balanced. Copy the site folder to a second server and create a site in IIS with the same settings as the previous server. No configuration changes are required.
  8. Test remote events by logging in to one of the servers and publishing a change to a page. The published changes appear on the other server. Use the search functionality from both servers to verify that it was configured correctly.

A hosting provider can help you load-balance incoming traffic between the servers. You can also use other software load-balancers, such as HAProxy, Network Load Balancing (NLB), or Application Request Routing (ARR).

📘

Note

The core parts of CMS do not use Session State but some functionality does, such as some of the Visitor Groups criteria. There are two approaches to enabling session state that depend on the sticky session feature (also known as session affinity) provided by a load-balancer that makes sure a user is reaching the same server combined with the default in-memory session state provider. You also can use an optimized provider for load-balancing, such as the built-in StateServer.

You should enable the ARR Affinity setting for applications in DXC to ensure proper functioning and saving of contents concerning cache invalidation by remote events. In Azure, go to the App Service where you want to make the change, select Settings > Application Settings, and enable ARR Affinity.

You can also set ARR Affinity in web.config as follows:

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Arr-Disable-Session-Affinity" value="false" />
    </customHeaders>
  </httpProtocol>
</system.webServer>