I have SQL Server running in a container with /var/opt/mssql saved on a volume. Now I want to clone said volume to a new volume and spin up another container using the cloned volume, but doing so gives me an Access Denied error when starting the second container.
To reproduce the problem
# Create container sql1 docker run --name sql1 -v sql1volume:/var/opt/mssql -p1501:1433 -d -e "ACCEPT_EULA=YES" -e "SA_PASSWORD=Pa55w.rd" mcr.microsoft.com/mssql/server:latest # Stop sql1 and clone sql1volume docker stop sql1 docker volume create sql2volume docker run --rm -i -t -v sql1volume:/original -v sql2volume:/clone alpine sh -c "cp -avr /original/* /clone" # Start new container using the cloned volume docker run --name sql2 -v sql2volume:/var/opt/mssql -p1502:1433 -d -e "ACCEPT_EULA=YES" -e "SA_PASSWORD=Pa55w.rd" mcr.microsoft.com/mssql/server:latest # Check out the logs from sql2 docker logs sql2
Logs will look like this
SQL Server 2022 will run as non-root by default. This container is running as user mssql. Your master database file is owned by mssql. To learn more visit https://go.microsoft.com/fwlink/?linkid=2099216. /opt/mssql/bin/sqlservr: Error: The system directory [/.system] could not be created. File: LinuxDirectory.cpp:420 [Status: 0xC0000022 Access Denied errno = 0xD(13) Permission denied]
Ok, error reproduced. Why does this happen?
It happens because Microsoft did good. Microsoft’s SQL Server-container images doesn’t run as root. They run as the user mssql, with user-id 10001. This is awesome, just as awesome as _not_ running SQL Server for Windows with a service account that is local admin on the server. However, the alpine container we use to clone the volume doesn’t run as user mssql. In fact, it doesn’t know who mssql is.
Thanks for the lesson. Now, how do we solve this mess?
It isn’t that hard to fix really. We need to create a user with user-id 10001 in our alpine container and we need to change ownership of the files in the container to this user. So let’s clean up and start cloning the volume again, this time creating the user too.
# cleanup docker rm sql2 docker volume rm sql2volume # Clone again docker run --rm -i -t -v sql1volume:/original -v sql2volume:/clone alpine sh -c "adduser -u 10001 mssql;cp -avr /original/* /clone;chown mssql -R /clone" docker run --name sql2 -v sql2volume:/var/opt/mssql -p1502:1433 -d -e "ACCEPT_EULA=YES" -e "SA_PASSWORD=Pa55w.rd" mcr.microsoft.com/mssql/server:latest # check logs docker logs sql2
Voila, logs look good!
2023-08-31 23:23:10.38 spid65s . Feature Status: PVS: 0. CTR: 0. ConcurrentPFSUpdate: 1. ConcurrentGAMUpdate: 1. ConcurrentSGAMUpdate: 1, CleanupUnderUserTransaction: 0. TranLevelPVS: 0 2023-08-31 23:23:10.38 spid65s Starting up database 'msdb'. 2023-08-31 23:23:10.49 spid47s . Feature Status: PVS: 0. CTR: 0. ConcurrentPFSUpdate: 1. ConcurrentGAMUpdate: 1. ConcurrentSGAMUpdate: 1, CleanupUnderUserTransaction: 0. TranLevelPVS: 0 2023-08-31 23:23:10.49 spid47s Starting up database 'tempdb'. 2023-08-31 23:23:10.81 spid47s The tempdb database has 8 data file(s). 2023-08-31 23:23:10.82 spid62s The Service Broker endpoint is in disabled or stopped state. 2023-08-31 23:23:10.82 spid62s The Database Mirroring endpoint is in disabled or stopped state. 2023-08-31 23:23:10.83 spid62s Service Broker manager has started. 2023-08-31 23:23:10.83 spid45s Recovery is complete. This is an informational message only. No user action is required.
This was actually pretty easy, but it wasn’t so easy to find it on the interwebs, thus this blog post. When running the updated cloning process, the alpine container (or rather the adduser command) will ask for a password. Just type anything there – it that password will be in the passwd file of the alpine container, which is thrown away as soon as it finished copying the files. But it’s a bit annoying – getting prompted for passwords makes it impossible to automate the process. There’s probably a flag for adduser to not change passwords but I din’t find it at first glance and it’s getting late here. Tomorrow perhaps…