I am experiencing an urgent request to prepare a Sitecore instance for the test of some external tools our prospect partners making demos for us. In good times, I'd, of course, spin up a proper PaaS / Kubernetes environment, however, I am occasionally out of control of any cloud subscription, and what is much more important - time! The deadline for such tasks is usually "yesterday", so I started thinking of potential "poor man's deployment" options.
Like many developers do, I also have a "server in a wardrobe", however, that is not a retired laptop but a proper high-speck machine that currently serves me as a hypervisor server plugged by a gigabit Google Fiber connection. My cat loves spending time there, and I am generally OK with that given she does not block the heat sink vents output:
This server runs on ltsc2022 kernel, which provides me additional performance benefits, as I wrote about in a previous post of running 10.3 in Process Isolation mode. So why not re-use the codebase from that same containerized Next.js starter kit for the sake of PoC?
Please note: you should not utilize this approach for hosting real-life projects or going for something bigger than a quick PoC of a demo showcase. The stability of a tunneled channel remains at the courtesy of a service provider, and it also may violate one's particular license, so please use it with care.
The next question comes of how do I make it accessible from the outer global internet so that people making demos can login from where they are and work with Sitecore as they would normally do. Typically, to make that happen I need to undertake three steps:
- Define a hostname and configure Sitecore to install with its subdomains.
- Generate a wildcard certificate for a domain name of the above hostname.
- Make required DNC changes to change A-record and subdomains to point public IP of that machine.
But wait a bit, do I have a public IP? Sadly, I don't, therefore, start looking for a variety of DynDNS options which still required more effort than I initially was going to commit into. Eventually, I remembered a specific class of tunneling software that serves exactly that purpose. From a wide range, LocalTunnel appeared to be the most promising free-to-use solution that some folks use to proxy out their basic sites for demos.
Looking at its features it looks very much attractive:
- it is totally free of charge
- does not require any registration/tokens
- ultrasimple installation with npm
- because of the above, it potentially can tunnel directly into containers
- gives you an option of a temporal claiming subdomain, if one is available
- allows mapping invalid SSL certificates
The typical installation and execution are ultra-simple:
npm install -g localtunnel
lt --port 8080
After the second command, LocalTunnel responds with a URL navigating which will tunnel your request to port 8080
of the host machine it was run at.
But how do I apply that knowledge to a complicated Sitecore installation, given that most of the Sitecore services in containers are behind Traefik, which also serves SSL offload point? In addition, the Identity Server requires a publically accessible URL to return the successfully authenticated request.
The more advanced call syntax looks as below:
lt --local-host HOST_ON_LOCAL_MACHINE --local-https --allow-invalid-cert --port 443 --subdomain SUBDOMAIN_TO_REQUEST
Basically, for Sitecore to operate from outside I must set it up in a way that external URLs match those URLs run locally at the host where LocalTunnel runs. With the above command, if a subdomain request is satisfied, it will be served by the URL https://SUBDOMAIN_TO_REQUEST.loca.lt
which leads to HOST_ON_LOCAL_MACHINE
on port 443
.
So, in a headless Sitecore we have four typical parts running on subdomains of a hostname served by a wildcard certificate:
- Content Management (aka Sitecore itself)
- Content Delivery
- Identity Server
- Rendering Host
OOB they are served by default with something like cm.YourProject.localhost
, cd.YourProject.localhost
, id.YourProject.localhost
and www.YourProject.localhost
correspondingly. In order to match HOST_ON_LOCAL_MACHINE
to SUBDOMAIN_TO_REQUEST
for the sake of this exercise, I choose the following hostnames for installation:
The scripts that create Next.js StarteKit Template and Init.ps1
script don't modify all the required hostname changes, so just in case you'll do it manually (recommended), I will name the locations to change
1. Init.ps1 - a block that makes and installs certificates (search by & $mkcert -install
)
2. Init.ps1 - a block that adds host file entries (search by Add-HostsEntry
)
3. Init.ps1 - a block that sets the environment variables (search Set-EnvFileVariable "CM_HOST")
4. Up.ps1 - authentication using Sitecore CLI (search by dotnet sitecore login
)
5. Up.ps1 - final execution in the browser (search by Start-Process
at the bottom of the file)
6. .env file - replace CM_HOST
, ID_HOST
, RENDERING_HOST
and CD_HOST
variables
7. Make sure Traefik config (docker\traefik\config\dynamic\certs_config.yaml
) references the correct certificate and key files
8. Create-jss-project.ps1
- features --layoutServiceHost
and --deployUrl
parameters of jss setup command
9. src\rendering\scjssconfig.json
10. src\rendering\src\temp\config.js
After the whole installation completes successfully and you see Sitecore CM and Rendering Host in the browser with alternated domain URLs.
Now you can start LocalTunnel:
lt --local-host identity.loca.lt --local-https --allow-invalid-cert --port 443 --subdomain identity
lt --local-host sitecore.loca.lt --local-https --allow-invalid-cert --port 443 --subdomain sitecore
lt --local-host rendering.loca.lt --local-https --allow-invalid-cert --port 443 --subdomain rendering
lt --local-host delivery.loca.lt --local-https --allow-invalid-cert --port 443 --subdomain delivery
On the first run from outside it may show you a notification screen that LocalTunnel serves given URL, and with relatively great piing, that's it.
I briefly tested it and it works well: no SSL issues, Experience Editor runs and allows to bring changes, then publishes them correctly so that they get reflected while browsing Rendering Host. All seems to work well and as expected!