Building a custom SQL Server 2017/2019 container on a windows server without internet
SQL Server 2019 Windows containers are still in preview as of this writing. If you don't join the Early Adopter program, you can create a custom image by starting with a Windows Server Core base image and then install SQL Server 2019 (and optionally CUs) to create a custom image.
The build is most easily done on a machine with internet connectivity to pull the base OS image from the public repro. You can then copy the image to the machine without an internet connection (or push it to a local private repro) and run it from there. Alternatively, pull, save, and load the OS images (steps 6-8) and run the build on the server.
Below is an example of the general steps to do this. My appologies for the formatting. I'm mardown-challenged.

create an empty directory on your Windows machine (e.g. C:\dockerfiles)
 MKDIR C:\dockerfiles


create a dockerfile in that directory with the following contents
 #Start with a Windows Server Core base image.
 #Note that the image with tag 'latest' is pulled by default, which could be older than the actual host OS version.
 #In this case, specify the version-specific to pull a later image compatible with the host OS version.

 FROM mcr.microsoft.com/windows/servercore:2004

 #Parameters for start.ps1:
 ENV sa_password=&quot;_&quot; \
     attach_dbs=&quot;[]&quot; \
     ACCEPT_EULA=&quot;_&quot; \
     sa_password_path=&quot;C:\ProgramData\Docker\secrets\sa-password&quot;

 #set default shell to PowerShell and set preferences:
 SHELL [&quot;powershell&quot;, &quot;-Command&quot;, &quot;$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';&quot;]

 #set current directory to root (C:\)
 WORKDIR /

 #copy SQL Server setup folder into build image for installation
 COPY setup setup

 #run setup to install
 RUN  \setup\setup.exe /q /ACTION=Install /INSTANCENAME=MSSQLSERVER /FEATURES=SQLEngine /UPDATEENABLED=0 /SQLSVCACCOUNT='NT AUTHORITY\System' /SQLSYSADMINACCOUNTS='BUILTIN\ADMINISTRATORS' /TCPENABLED=1 /NPENABLED=0 /IACCEPTSQLSERVERLICENSETERMS

 #remove setup files
 RUN  Remove-Item -Force -Recurse -Path 'setup'

 #create empty directory (C:\SqlFiles) for user database files
 RUN &quot;mkdir SqlFiles | Out-Null&quot;

 #stop SQL Server service, configure TCP/IP, allow SQL authentication, and set default file locations
 RUN stop-service MSSQLSERVER ; \
         set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql15.MSSQLSERVER\mssqlserver\supersocketnetlib\tcp\ipall' -name tcpdynamicports -value '' ; \
         set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql15.MSSQLSERVER\mssqlserver\supersocketnetlib\tcp\ipall' -name tcpport -value 1433 ; \
         set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql15.MSSQLSERVER\mssqlserver\' -name LoginMode -value 2 ; \
         set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql15.MSSQLSERVER\mssqlserver\' -name BackupDirectory -value 'C:\SqlFiles' ; \
         set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql15.MSSQLSERVER\mssqlserver\' -name DefaultData -value 'C:\SqlFiles' ; \
         set-itemproperty -path 'HKLM:\software\microsoft\microsoft sql server\mssql15.MSSQLSERVER\mssqlserver\' -name DefaultLog -value 'C:\SqlFiles' ;

 #set command used for container health check status
 HEALTHCHECK CMD [ &quot;sqlcmd&quot;, &quot;-Q&quot;, &quot;SELECT 1;&quot; ]

 &amp;#35;copy startup script to image
 COPY start.ps1 /

 #specify command invoked when container is run from this image
 CMD .\start -sa_password $env:sa_password -ACCEPT_EULA $env:ACCEPT_EULA -attach_dbs \&quot;$env:attach_dbs\&quot; -Verbose


create start.ps1 file in build directory (e.g. C:\dockerfiles\start.ps1). Below is an example script similar to the one I use.
 # The script sets the sa password and start the SQL Service
 # Also it attaches additional database from the disk
 # The format for attach_dbs

 param(
 [Parameter(Mandatory=$false)]
 [string]$sa_password,

 [Parameter(Mandatory=$false)]
 [string]$ACCEPT_EULA,

 [Parameter(Mandatory=$false)]
 [string]$attach_dbs
 )


 if($ACCEPT_EULA -ne &quot;Y&quot;)
 {
     Write-Verbose &quot;ERROR: You must accept the End User License Agreement before this container can start.&quot;
     Write-Verbose &quot;Set the environment variable ACCEPT_EULA to 'Y' if you accept the agreement.&quot;

     exit 1
 }

 #start the service
 Write-Verbose &quot;Starting SQL Server&quot;
 Start-Service MSSQLSERVER

 if($sa_password -eq &quot;_&quot;) {
     if (Test-Path $env:sa_password_path) {
         $sa_password = Get-Content -Raw $secretPath
     }
     else {
         Write-Verbose &quot;WARN: Using default SA password, secret file not found at: $secretPath&quot;
     }
 }

 if($sa_password -ne &quot;_&quot;)
 {
     Write-Verbose &quot;Changing SA login credentials&quot;
     $sqlcmd = &quot;ALTER LOGIN sa with password=&quot; +&quot;'&quot; + $sa_password + &quot;'&quot; + &quot;;ALTER LOGIN sa ENABLE;&quot;
     &amp; sqlcmd -Q $sqlcmd
 }

 $attach_dbs_cleaned = $attach_dbs.TrimStart('\\').TrimEnd('\\')

 $dbs = $attach_dbs_cleaned | ConvertFrom-Json

 if ($null -ne $dbs -And $dbs.Length -gt 0)
 {
     Write-Verbose &quot;Attaching $($dbs.Length) database(s)&quot;

     Foreach($db in $dbs) 
     {            
         $files = @();
         Foreach($file in $db.dbFiles)
         {
             $files += &quot;(FILENAME = N'$($file)')&quot;;           
         }

         $files = $files -join &quot;,&quot;
         $sqlcmd = &quot;IF EXISTS (SELECT 1 FROM SYS.DATABASES WHERE NAME = '&quot; + $($db.dbName) + &quot;') BEGIN EXEC sp_detach_db [$($db.dbName)] END;CREATE DATABASE [$($db.dbName)] ON $($files) FOR ATTACH;&quot;

         Write-Verbose &quot;Invoke-Sqlcmd -Query $($sqlcmd)&quot;
         &amp; sqlcmd -Q $sqlcmd
     }
 }

 Write-Verbose &quot;Started SQL Server.&quot;

 Write-Verbose &quot;Changing SQL Server server name to $($env:COMPUTERNAME)&quot;
 $sqlcmd = &quot;EXEC sp_dropserver @@SERVERNAME;EXEC sp_addserver N'$env:COMPUTERNAME', 'local';&quot;
 &amp; sqlcmd -Q $sqlcmd

 Write-Verbose &quot;Restarting SQL Server.&quot;
 Stop-Service MSSQLSERVER
 Start-Service MSSQLSERVER
 Write-Verbose &quot;Restarted SQL Server.&quot;

 Write-Verbose &quot;Tailing log...&quot;
 Get-Content -Path &quot;C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\Log\ERRORLOG&quot; -Wait


copy files from SQL 2019 installation media to a setup subdirectory (e.g. C:\dockerfiles\setup)

run docker build in the contest of the build directory (e.g. C:\dockerfiles) to build and tag the custom image
 docker build -t mssql-2019-windows-dev-rtm-example .


save the image to a tar file
 docker save --output &quot;C:\temp\mssql-2019-windows-dev-rtm-example.tar&quot; mssql-2019-windows-dev-rtm-example


copy the image to the Windows server
 copy &quot;C:\temp\mssql-2019-windows-dev-rtm-example.tar&quot; &quot;\\YourServer\c$\temp\mssql-2019-windows-dev-rtm-example.tar&quot;


load the image from the tar file on the Windows server machine:
 docker load --input &quot;C:\temp\mssql-2019-windows-dev-rtm-example.tar&quot;


example to run the image
 docker run -d -p 14331:1433 -h sql2019 --name sql2019 -e &quot;sa_password=y0ourS@pAssword&quot; -e &quot;ACCEPT_EULA=Y&quot; -e attach_dbs=&quot;&quot; --rm mssql-2019-windows-dev-rtm-example:latest



Be aware Windows containers are not yet supported on production and you are on your own with SQL 2019 images at this time unless you join the EA preview.