---
title: "Deploying items"
slug: "deploying-items"
abstract: "Explains how you deploy Sitecore items when you use Docker."
---
# Deploying items

This topic explains Sitecore item package and deployment automation strategies on Docker. There are different ways to do this, depending on the item serialization tool you use.

<details>
<summary><h2>Sitecore CLI and Sitecore Content Serialization</h2></summary>

The Sitecore CLI is optimized for scripted interaction with a remote Sitecore instance. It can be used to package items during your continuous integration process, and to install those packages during your deployment and delivery process. Automating item deployment into a Sitecore environment with the Sitecore CLI is no different when using containers.

The [Sitecore CLI documentation](https://doc.sitecore.com/xp/en/developers/101/developer-tools/sitecore-content-serialization.html "Sitecore Content Serialization") has more details, and the [Sitecore module reference](https://doc.sitecore.com/xp/en/developers/101/developer-tools/sitecore-module-reference.html "Sitecore module reference") has information about installing the required Sitecore Management Services module in your CM container image.

</details>

<details>
<summary><h2>Sitecore TDS</h2></summary>

[Sitecore TDS](https://www.teamdevelopmentforsitecore.com/) has a number of built-in tools that facilitate item packaging and deployment. The general approach is to use these tools to create item packages during your solution build, and then add them to the Content Management (CM) image when building your Sitecore runtime images.

The [Helix.Examples repository on GitHub](https://github.com/Sitecore/Helix.Examples) has a complete example.

### Sitecore TDS Project Configuration

This example takes advantage of two Sitecore TDS features: [Build Output](https://hedgehogdevelopment.github.io/tds/chapter4.html#build) and [WebDeploy Packages](https://hedgehogdevelopment.github.io/tds/chapter4.html#webdeploy-package). These are settings configured on the TDS project property pages:

- **Build Output Path** - Located on the **Build** page, you set this for the `Release` build configuration. Specify a path shared by all TDS projects. In our example, this is set to `..\..\TdsGeneratedPackages\Release\`.
- **Build WebDeploy package** - Located on the **WebDeploy Package** page, select this for the `Release` build configuration. Specify a *Package Name*, and select *Item only Package* for *Code and Item Packaging Options*.

The following assumes you have configured your TDS projects accordingly.

### Configure in solution build

Sitecore TDS requires licensing environment variables for Docker or cloud builds:

- In your solution build Dockerfile, declare `ARG`s for these at the start of your code compilation and build stage (`builder` in the examples):

    ```

    ARG TDS_Owner
    ARG TDS_Key
    ```

    The location within the Dockerfile is important due to `ARG` [scope](https://docs.docker.com/engine/reference/builder/#scope). If you instead declare these in a previous build stage, the values are empty when used in the `builder` stage. Also note that `ARG` is used instead of `ENV` because these are only used for building the image.

    These values are populated for the `solution` service in the `docker-compose.override.yml` file, for example:

    ```

    solution:
      image: ${REGISTRY}${COMPOSE_PROJECT_NAME}-solution:${VERSION:-latest}
      build:
        context: .
        args:
          BASE_IMAGE: ${SOLUTION_BASE_IMAGE}
          BUILD_IMAGE: ${SOLUTION_BUILD_IMAGE}
          TDS_Owner: ${TDS_OWNER}
          TDS_Key: ${TDS_KEY}
      scale: 0
    ```

    You can have the `TDS_OWNER` and `TDS_KEY` values in the [environment file](https://docs.docker.com/compose/env-file/) (.env), but usually you specify them as system environment variables on local development machines and as secrets on your build server (for security purposes).

- In the solution build Dockerfile, you can simplify the `msbuild` instruction, and instead rely on the build output configured in your TDS projects:

    ```text
    RUN msbuild /p:Configuration=Release
    ```

    The build output lands at the location specified in the TDS project (the *Build Output Path*), relative to the `builder`'s `WORKDIR`. In the example, this means build output is:

    - `\build\TdsGeneratedPackages\Release` (files)
    - `\build\TdsGeneratedPackages\WebDeploy_Release` (WDP item packages)

    You copy these in from the `builder` stage to the final image with the following structure:

    - `\artifacts\website`
    - `\artifacts\packages`
    - `\artifacts\transforms`
- You can adjust the final instructions to:

    ```

    WORKDIR C:\artifacts
    COPY --from=builder \build\TdsGeneratedPackages\Release .\website\
    COPY --from=builder \build\TdsGeneratedPackages\WebDeploy_Release .\packages\
    COPY --from=builder C:\out\transforms .\transforms\
    ```

### Add to Sitecore CM runtime image

When you have packaged and included items with the `solution` image, you can add them to the Content Management (CM) image when you build your Sitecore runtime images.

At the end of the Dockerfile for your *cm* service, add instructions to deploy your item packages. TDS has 2 options for handling this:

- Option 1: Deploy on container create

    This allows Sitecore TDS to install item packages on site startup. It uses a [built-in feature of TDS](https://hedgehogdevelopment.github.io/tds/chapter7.html#deployment-process). However, you must be aware that this happens every time a container is created using this image:

    ```

    COPY --from=solution \artifacts\packages\ \temp\
    RUN Get-ChildItem -Path 'C:\\temp\\*.wdp.zip' | % { Expand-Archive -Path $_.FullName -DestinationPath 'C:\\temp' -Force; }; `
        Move-Item -Path 'C:\\temp\\Content\\Website\\Bin\*' -Destination .\bin -Force; `
        Move-Item -Path 'C:\\temp\\Content\\Website\\temp\*' -Destination .\temp -Force; `
        Remove-Item -Path 'C:\\temp' -Recurse -Force; `
        # Ensure TDS has permissions to delete items after install
        cmd /C icacls .\temp\WebDeployItems /grant 'IIS AppPool\DefaultAppPool:(OI)(CI)M';
    ```

- Option 2: Deploy on-demand

    Use the `Deploy-TdsWdpPackages.ps1` script from the `tooling` image. Copy it in along with your packages:

    ```

    COPY --from=tooling \tools\scripts\Deploy-TdsWdpPackages.ps1 \install\Deploy-TdsWdpPackages.ps1
    COPY --from=solution \artifacts\packages\ \install\packages\
    ```

    You can then invoke `Deploy-TdsWdpPackages.ps1` on the container on-demand with the following script:

    ```text
    docker exec <container> powershell -command "C:\install\Deploy-TdsWdpPackages.ps1"
    ```

</details>

<details>
<summary><h2>Unicorn</h2></summary>

> [!important]
> Unicorn is a third-party open source tool which is not supported by Sitecore Support. These instructions are provided as guidance only, for the convenience of Unicorn users.

Unlike the other serialization tools described here, [Unicorn](https://github.com/SitecoreUnicorn/Unicorn) does not have a built-in packaging capability, and it runs in-process with the Sitecore platform. Therefore, the serialized items in your Unicorn configuration need to be present in the filesystem of the Content Management instance wether you want to sync them. When you use Docker this is easy because you can copy the items during your container build. Unicorn also provides a [PowerShell module](https://github.com/SitecoreUnicorn/Unicorn/tree/master/doc/PowerShell%20Remote%20Scripting) to trigger item synchronization. This is convenient for automating item deployment when using containers.

The following steps assume you are already using Docker to build your solution and create a custom Sitecore images. Refer to the [Helix.Examples repository on GitHub](https://github.com/Sitecore/Helix.Examples) for a complete example.

### Add serialized items to your solution artifacts

When you use Unicorn, you typically place the serialized items within your Sitecore solution source, and organize them in a structure that is defined by your Unicorn configuration, potentially following [Sitecore Helix practices](https://helix.sitecore.net). You can copy all these serialized items during your solution build, but retaining their directory structure is important. Robocopy is one easy option for accomplishing this:

```

# Copy serialized items, retaining directory structure
RUN Invoke-Expression 'robocopy C:\build\src C:\out\items /s /ndl /njh /njs *.yml'

# ... later in your artifacts build stage
WORKDIR C:\artifacts
COPY --from=builder c:\out\items .\items\

```

### Build items and deployment scripts into your CM image

Now that you have items as build artifacts in your solution image, copy them into your CM image, just as you have done previously with your `msbuild` output. Additionally, place the files needed for Unicorn remoting in the CM image, so that you can later run a scripted sync from a CM container:

1. A `unicorn` folder in your CM [build context](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#understand-build-context) must contain the [files needed for Unicorn remote scripting](https://github.com/SitecoreUnicorn/Unicorn/tree/master/doc/PowerShell%20Remote%20Scripting):
    - `MicroCHAP.dll`
    - `Unicorn.psm1`
    - A custom `sync.ps1`
2. The `sync.ps1` script in this case must contain a standard invocation of `Sync-Unicorn`, and use an environment variable to populate the Unicorn shared secret:

    ```

    $ScriptPath = Split-Path $MyInvocation.MyCommand.Path
    Import-Module $ScriptPath\Unicorn.psm1
    Sync-Unicorn -ControlPanelUrl 'http://localhost/unicorn.aspx' -SharedSecret $env:UNICORN_SHARED_SECRET
    ```

3. In your CM Dockerfile, copy in this folder and the serialized items artifact, and set an environment variable that you can use later for configuring Unicorn:

    ```

    # Set the default location for serialized items
    # This value will be used in our Unicorn configuration
    ENV ITEM_SYNC_LOCATION c:\items

    # Copy serialized items and Unicorn sync script
    COPY --from=solution \artifacts\items\ \items\
    COPY .\unicorn \unicorn\
    ```

### Configure Unicorn and your Docker environment

The base filesystem path used for Unicorn sync is typically set in Sitecore configuration using an `sc.variable` called `sourceFolder`:

1. Populate this value from the environment variable defined above in our `Dockerfile`.

    ```text
    <sc.variable name="sourceFolder" value="$(env:ITEM_SYNC_LOCATION)" />
    ```

2. Populate the Unicorn shared secret from an environment variable as well, allowing you to use the same environment variable as you did in `sync.ps1` previously:

    ```xml
    <authenticationProvider type="Unicorn.ControlPanel.Security.ChapAuthenticationProvider, Unicorn">
      <SharedSecret>$(env:UNICORN_SHARED_SECRET)</SharedSecret>
    </authenticationProvider>
    ```

3. Define this environment variable in your `docker-compose.override.yml` and `.env`:

    ```

    cm:
        [...]
        environment:
          UNICORN_SHARED_SECRET: ${UNICORN_SHARED_SECRET}
    ```

    ```text
    UNICORN_SHARED_SECRET=your-secret-here
    ```

### Run a sync

At this point, you have everything you need to trigger an item sync with Unicorn:

- Serialized items on the filesystem of your CM container
- Unicorn configured to use this path as its item source
- The Unicorn shared secret configured via an environment variable
- A PowerShell script to trigger the sync

When you want to deploy the items, whether manually or within your delivery pipeline, use `docker exec`, or the equivalent within your production container orchestrator:

```text
docker exec <container> powershell -command "c:\unicorn\sync.ps1"

```

</details>

<details>
<summary><h2>Further reading</h2></summary>

- [Sitecore Command Line Interface](https://doc.sitecore.com/xp/en/developers/101/developer-tools/sitecore-command-line-interface.html "Sitecore Command Line Interface")
- [Sitecore TDS documentation](http://hedgehogdevelopment.github.io/tds/)
- [Sync items with a running container](https://doc.sitecore.com/xp/en/developers/101/developer-tools/sync-items-with-a-running-container.html "Sync items with a running container")

</details>