Low-effort Solr installs

I’m sure I’ve said before that any task you have to do more than once is worth automating. Well recently I’ve found myself needing to install Solr in a variety of places – so obviously my mind turned to automation. There are lots of ways this can be approached, and some people have already had a go at it for their own needs, but here’s my take.

Hey reader!
After many years, I’ve finally decided to start moving off WordPress and onto a blog I host myself. This post is now available at “https://blog.jermdavis.dev/posts/2017/low-effort-solr-installs“. Eventually this version will be retired so please update links on any sites you control, to avoid 404s in the future.

Edited to add: Kam Figy makes the valid points that the downloads should really use HTTPS, and it would be even easier if the script added a host entry if necessary. I’ve updated this based on those suggestions…

The problem I wanted to solve

I needed to be able to easily set up development instances of different versions of Solr. My company has tended to use NSSM as its approach to running Solr as a service, so I wanted to stick with that. Some of these Solr instances need to work over HTTPS, so I also wanted to be able to set up certificates for them. And I needed it to work in a scenario where I had to run multiple versions of Solr at once.

The instances of Solr were required for v8 Sitecore projects, so the script needed to work without SIF. That meant plain PowerShell script seemed the best way to go. So, firing up the PowerShell ISE…

Getting started…

The first thing to note is that installing services requires admin privileges. That means the script should probably report an error if it’s run from a session that’s not elevated. One approach to testing that state looks like:

$elevated = [bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544")
if($elevated -eq $false)
    throw "In order to install services, please run this script elevated."

That checks if the current user has the Administrator security principal, and if not, throws an error to stop the script.

Now the script is going to need some input from the user to decide what it needs to do:

    $installFolder = "c:\solr",
    $nssmVersion = "2.24",
    $solrVersion = "6.6.2",
    $solrPort = "8983",
    $solrHost = "localhost",
    $solrSSL = $true,
    $JREVersion = "1.8.0_151"

It needs to know where to put the stuff being installed via $installFolder, and it needs to know versions of the software we want to install via $nssmVersion and $solrVersion. Solr needs a port number, a host name and whether it should used HTTPS or not, via $solrVersion, $solrHost and $solrSSL. And finally we need to know what version of the Java runtime Solr should use when it runs if there’s not global setting for this, via $JREVersion.

Based on those settings, the script can compute a few other values that will be used later:

$JREPath = "C:\Program Files (x86)\Java\jre$JREVersion"
$solrName = "solr-$solrVersion"
$solrRoot = "$installFolder\$solrName"
$nssmRoot = "$installFolder\nssm-$nssmVersion"
$solrPackage = "https://archive.apache.org/dist/lucene/solr/$solrVersion/$solrName.zip"
$nssmPackage = "https://nssm.cc/release/nssm-$nssmVersion.zip"
$downloadFolder = "~\Downloads"

Edited to add: Note that the $JREPath here specifies the Program Files (x86) folder. That’s only appropriate if you’re using the 32bit java runtime. If you are using the 64bit version you will need to change this path so that it points to the right location for your machine.

Next we need to ensure that Solr and NSSM are downloaded and unzipped to the right place. Since the same task has to happen for two things, that suggests a function… To avoid downloading and unzipping when it’s not necessary this should check if the destination folder we’re using exists, and do nothing if it does. If it doesn’t exist then it should check if the zip file exists before downloading it:

function downloadAndUnzipIfRequired

    if(!(Test-Path -Path $toolFolder))
        if(!(Test-Path -Path $toolZip))
            Write-Host "Downloading $toolName..."
            Start-BitsTransfer -Source $toolSourceFile -Destination $toolZip

        Write-Host "Extracting $toolName to $toolFolder..."
        Expand-Archive $toolZip -DestinationPath $installRoot

That can be called once for each of the zip files we want downloaded and extracted:

$solrZip = "$downloadFolder\$solrName.zip"
downloadAndUnzipIfRequired "Solr" $solrRoot $solrZip $solrPackage $installFolder

$nssmZip = "$downloadFolder\nssm-$nssmVersion.zip"
downloadAndUnzipIfRequired "NSSM" $nssmRoot $nssmZip $nssmPackage $installFolder

Solr is a Java application, so it needs to know which Java runtime to make use of. You can set this in the Solr config files, but my preference is to ensure that there’s a global environment variable to specify this, as that makes it easier to change it for everything when the runtime gets updated. So if the system doesn’t have the right value for the JAVA_HOME environment variable, the script needs to set that to match the version of Java the parameter specifies:

$jreVal = [Environment]::GetEnvironmentVariable("JAVA_HOME", [EnvironmentVariableTarget]::Machine)
if($jreVal -ne $JREPath)
    Write-Host "Setting JAVA_HOME environment variable"
    [Environment]::SetEnvironmentVariable("JAVA_HOME", $JREPath, [EnvironmentVariableTarget]::Machine)

The next thing we need, is to deal with the configuration for Solr. If the parameters for the script say it’s using HTTP, that’s pretty easy:

if($solrSSL -eq $false)
    if(!(Test-Path -Path "$solrRoot\bin\solr.in.cmd.old"))
        Write-Host "Rewriting solr config"

        $cfg = Get-Content "$solrRoot\bin\solr.in.cmd"
        Rename-Item "$solrRoot\bin\solr.in.cmd" "$solrRoot\bin\solr.in.cmd.old"
        $newCfg = $newCfg | % { $_ -replace "REM set SOLR_HOST=", "set SOLR_HOST=$solrHost" }
        $newCfg | Set-Content "$solrRoot\bin\solr.in.cmd"

Most of the config parameters for Solr can be set by amending the defaults in the solr.in.cmd file. So the script can read that, find the line that needs updating, and replace it with the right value. Before changing anything it backs up the original file – which is also an easy way to tell if the changes have already been made.

If the host name we’re using for Solr here isn’t “localhost” then it’s proably also useful to add that to the machine’s hosts file:

if($solrHost -ne "localhost")
    $hostFileName = "c:\\windows\system32\drivers\etc\hosts"
    $hostFile = [System.Io.File]::ReadAllText($hostFileName)
    if(!($hostFile -like "*$solrHost*"))
        Write-Host "Updating host file"
        "`r`n127.0.0.1`t$solrHost" | Add-Content $hostFileName

Life is a bit more complex if HTTPS is enabled, as the certificates need setting up as well. That involves four things: Generating a certificate, exporting it so Solr can make use of it for its web server, trusting the certificate and finally more config changes.

Being a Java application, Solr’s default is to use the not-very-Windows-friendly “JKS” export format for the certificates. If you prefer that approach, the code is out there. But newer versions of Solr can use “PFX” files, which is easier to deal with in a PowerShell-native way.

if($solrSSL -eq $true)
    $existingCert = Get-ChildItem Cert:\LocalMachine\Root | where FriendlyName -eq "$solrName"
        Write-Host "Creating & trusting an new SSL Cert for $solrHost"

        $cert = New-SelfSignedCertificate -FriendlyName "$solrName" -DnsName "$solrHost" -CertStoreLocation "cert:\LocalMachine" -NotAfter (Get-Date).AddYears(10)

        $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "Root","LocalMachine"

        # remove the untrusted copy of the cert
        $cert | Remove-Item

    if(!(Test-Path -Path "$solrRoot\server\etc\solr-ssl.keystore.pfx"))
        Write-Host "Exporting cert for Solr to use"

        $cert = Get-ChildItem Cert:\LocalMachine\Root | where FriendlyName -eq "$solrName"
        $certStore = "$solrRoot\server\etc\solr-ssl.keystore.pfx"
        $certPwd = ConvertTo-SecureString -String "secret" -Force -AsPlainText
        $cert | Export-PfxCertificate -FilePath $certStore -Password $certpwd | Out-Null

    if(!(Test-Path -Path "$solrRoot\bin\solr.in.cmd.old"))
        Write-Host "Rewriting solr config"

        $cfg = Get-Content "$solrRoot\bin\solr.in.cmd"
        Rename-Item "$solrRoot\bin\solr.in.cmd" "$solrRoot\bin\solr.in.cmd.old"
        $newCfg = $cfg | % { $_ -replace "REM set SOLR_SSL_KEY_STORE=etc/solr-ssl.keystore.jks", "set SOLR_SSL_KEY_STORE=$certStore" }
        $newCfg = $newCfg | % { $_ -replace "REM set SOLR_SSL_KEY_STORE_PASSWORD=secret", "set SOLR_SSL_KEY_STORE_PASSWORD=secret" }
        $newCfg = $newCfg | % { $_ -replace "REM set SOLR_SSL_TRUST_STORE=etc/solr-ssl.keystore.jks", "set SOLR_SSL_TRUST_STORE=$certStore" }
        $newCfg = $newCfg | % { $_ -replace "REM set SOLR_SSL_TRUST_STORE_PASSWORD=secret", "set SOLR_SSL_TRUST_STORE_PASSWORD=secret" }
        $newCfg = $newCfg | % { $_ -replace "REM set SOLR_HOST=", "set SOLR_HOST=$solrHost" }
        $newCfg | Set-Content "$solrRoot\bin\solr.in.cmd"

If a certificate for the host name doesn’t already exist in the trusted store, then the code can use New-SelfSignedCertificate to make one. It can then use the certificate store API to copy the certificate to the root certificate bit of the store, before deleting the untrusted copy.

If no certificate exists in the Solr folders then it can be exported to there in PFX format. This script is only aimed at development environments, so I’ve not bothered with a strong password for this file – but that would be easy to add if you needed it.

Finally, the same tactic for updating the config file can set the host, and the relevant settings for SSL.

With all that in place, Solr is ready to be started, so the service is the next thing to sort out:

$svc = Get-Service "$solrName" -ErrorAction SilentlyContinue
    Write-Host "Installing Solr service"
    &"$installFolder\nssm-$nssmVersion\win64\nssm.exe" install "$solrName" "$solrRoot\bin\solr.cmd" "-f" "-p $solrPort"
    $svc = Get-Service "$solrName" -ErrorAction SilentlyContinue
if($svc.Status -ne "Running")
    Write-Host "Starting Solr service"
    Start-Service "$solrName"

It checks if a correctly named service exists, and if not gets NSSM to set one up. If the service isn’t running it gets started.

Finally, to prove it worked, the script opens the Solr UI in your default browser:

$protocol = "http"
if($solrSSL -eq $true)
    $protocol = "https"
Invoke-Expression "start $($protocol)://$($solrHost):$solrPort/solr/#/"

And there’s Solr ready to configure for use with Sitecore, or whatever other uses you have for it…

The complete source is available as a Gist if you want to make use of this in your work.

Having put that together, I’ve started thinking about how these techniques could be adapted to working with Sitecore’s SIF framework. But that’s a blog post for another day, as there’s a pile of refactoring to do, in order to make it fit in with the style of SIF’s approach. [Edited to add: That other day came – the post is avalable here]

40 thoughts on “Low-effort Solr installs

  1. Hey Jeremy,
    nice post! The one thing I have been solving recently is the inability of the local XP0 install script from Sitecore to restart a 6.6.2 Solr instance running as a service using NSSM. Did you also face something similar? I am now exploring ways how to containerize Solr just to get rid of installing it altogether…

    • Thanks! I’ve not seen that issue – but I’ve not tried this as part of a scripted install of V9 yet, so maybe that fun is yet to come… I’m hoping to get time to do a follow-up post about integrating this type of install (but rewritten somewhat to fit with SIF’s style) in the future, so if I do see that issue, I’ll look into sorting it out…

    • Well – I have figured out where the problem was. Geoff – I was using the prerelease in 64bits mode. The problem was, that I was starting Solr without the -f (foreground) parameter. That meant, that when the script tried to restart the service, it essentially tried to spin up another instance of Solr that conflicted on the port number with the one already running in the background. Duh… So the trick is to start always with the -f parameter.

  2. Pingback: How to install Solr as a Windows Service | Sitecore Blog
  3. Pingback: Sitecore 9 SIF installation error : ERROR_SCRIPTDOM_NEEDED_FOR_SQL_PROVIDER | Naveed's Blog
  4. Pingback: Jak nainstalovat Solr jako Windows Službu | Sitecore Blog
    • My pleasure. I was working on Windows 10 for this, so I’m not surprised it needed tweaking for Windows 8. If you want to share what you had to do to make it work I’ll add a link to that, as I suspect others would find it useful.

  5. Pingback: Solr installs with SIF | Jeremy Davis
  6. Pingback: Installing Sitecore 9 – How I did it | Visions In Code
  7. Thanks, Jeremy, for this contribution. It is truly helpful. I’m having issues with SSL. The certificates seem to be properly installed. It appears under “Certificates – Local Computer>Trusted Root Certification Authorities>Certificates” and “Certificates – Local Computer>Intermediate Certification Authorities>Certificates”. The solr cmd file “solr.in.cmd” also has the required SSL lines:

    REM Uncomment to set SSL-related system properties
    REM Be sure to update the paths to the correct keystore for your environment
    set SOLR_SSL_KEY_STORE=c:\solr\solr-6.6.2\server\etc\solr-ssl.keystore.pfx
    set SOLR_SSL_TRUST_STORE=c:\solr\solr-6.6.2\server\etc\solr-ssl.keystore.pfx

    However, the browsers (Chrome and IE) tells the Solr site is not secure. Not sure why.

    If anyone had this issue before and can share the solution would be very much appreciated.

    Kind Regards,


    • After some review, I found the source of the issue. I was trying to access solr with “https://localhost:8983/solr/#/” and the certificate was set up to work with “https://solr:8983/solr/#/”. Using the later URL did the trick.

      I hope this helps someone else.


  8. Pingback: Sitecore 9 SIF – WebDeploy Gets What WebDeploy Wants – Sitecore Architecture
  9. Pingback: #Sitecore 9 installation with existing SSL – XtremDev
  10. Pingback: Sitecore 9 on Windows 8.1, my experience – XtremDev
  11. Pingback: Install Sitecore 9 Update 1 - in 4 Simple Steps - Flux Digital Blog
  12. Jeremy,
    Thanks for this script. It saved so much time. One thing I noticed in the latest script that I copied from the gist,
    $JREPath = “C:\Program Files\Java\jre$JREVersion”
    is not “C:\Program Files (x86)\Java\jre$JREVersion”, which you have it right in this post. The solr service failed to start as the JAVA_HOME environment variable was incorrect.


    • Ah – I suspect that depends on whether you installed 32bit or 64bit java?
      Thanks for pointing that out though – I’ll make myself a note to update the post and the gist with a comment about that issue.

  13. Pingback: INSTALL SITECORE 9 UPDATE 1 WITH SIF FOR LAZY DEV’S – Sitecore Trends
  14. Pingback: Install As Many Solr Instances As you Want on your Local Dev Machine for Sitecore 9 – Sitecore Endeavor
  15. Pingback: Sitecore 9.1 Initial Release Installation Guide for Development Machine – straight to the core
  16. Pingback: How to install Sitecore 9 on Windows 8.1 – XtremDev – Sebastian Tecsi | Sitecore Blog
  17. Pingback: Surviving the developers’ Javapocalypse | Jeremy Davis
  18. Pingback: You can have “Solr as a Service” now… | Jeremy Davis
  19. Pingback: Creating Multiple Uniquely Named Solr Instances For Sitecore Development - Flux Digital Blog
  20. Pingback: Installing SOLR 7.2.1 (on prem) for Sitecore 9.1 | Kayee
  21. Pingback: Install Sitecore 9.1 quickly in 5 easy steps - Sitecore Explorations
  22. Pingback: Solr Install via Desired State Configuration, Lowest-Effort Solr Install? – Sitecore Rap
  23. Pingback: Installing SOLR 7.3.1 (on prem) with OpenJDK for Sitecore 9.1 | Kayee
  24. Pingback: Self-signed Certificates with Solr Cloud and Sitecore 9.1 - Sixteen Pieces of Flair
  25. Pingback: Installing Solr for Sitecore with OpenJDK – Ken's Thoughts
  26. Pingback: Installing Solr 7.5 for Sitecore 9.2 – straight to the core
  27. Pingback: Bonus chatter: I can’t let my 200th post pass without comment… | Jeremy Davis
  28. Pingback: Installing Solr 8.1.1 with OpenJDK for Sitecore 9.3 | Kayee
  29. Pingback: Sitecore 9.2 Installation with SIA | CMS Sky
  30. Pingback: Revising that old Solr install script | Jeremy Davis
  31. Pingback: Use PowerShell script to install SOLR | Sitecore Diaries

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.