Migrating urlsl.me and its Database to a New Server with docker

I have neglected to move my url shortening site urlsl.me or urlshortlink.me to my new server so I'm still paying for the old server, so I'll be detailing the process of migrating that service here.

First, I backed up the production database on the old server using pgAdmin

https://i.imgur.com/nwHLNSp.png

Then I recreated the database on my new server, so that I could then restore the data using the backup I had just made.

https://i.imgur.com/kbIpfMm.png

I took a look at the source code before I added the Dockerfile. I decided my backend was horrendous, so I took time to redo it. I created a service IShortLinkManager which does everything the site needs for now.

public interface IShortLinkManager
{
    public Task<ShortLink> CreateNewShortUrl(string longUrl);
    public Task<ShortLink> GetShortLink(string key);
}

The implementation consumes a database context from EF Core UrlslContext. I call the database as needed in the method implementations for storing or retrieving a ShortLink.

public class ShortLinkManager : IShortLinkManager
{
    private UrlslContext dbContext { get; init; }
    public ShortLinkManager(UrlslContext urlslContext)
    {
        dbContext = urlslContext;
    }
    public async Task<ShortLink> GetShortLink(string shortKey) =>
        await dbContext.ShortLink.SingleOrDefaultAsync(s => s.Short == shortKey).ConfigureAwait(false);
}

In Startup.cs dependency injection is used so that ShortLinkManager is a service that can be consumed by the asp.net controller. ShortLinkManager also has the dependency UrlslContext, so I made that a service as well.

services.AddDbContext<UrlslContext>(o => o.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<IShortLinkManager, ShortLinkManager>();

Then auto-generated a Dockerfile with VS and pulled my changes down on the new server.

FROM mcr.microsoft.com/dotnet/aspnet:5.0-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:5.0-buster-slim AS build
WORKDIR /src
COPY ["UrlShortLink/UrlShortLink.csproj", "UrlShortLink/"]
COPY ["DataManager/DataManager.csproj", "DataManager/"]
RUN dotnet restore "UrlShortLink/UrlShortLink.csproj"
COPY . .
WORKDIR "/src/UrlShortLink"
RUN dotnet build "UrlShortLink.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "UrlShortLink.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "UrlShortLink.dll"]

https://i.imgur.com/mZ8P5Rf.png

After that I ran two simple commands to build the docker image and run a new container

sudo docker build -t fc/urlsl:2021-x-xx -t fc/urlsl:latest -f UrlShortLink/Dockerfile .

sudo docker run -d --name urlsl -p someport:80 --restart=always --add-host host.docker.internal:host-gateway fc/urlsl:latest

Using sudo docker ps showed me that the app is running:

https://i.imgur.com/YFVXnNw.png

At this point I made a configuration file for nginx and softlinked it to sites-enabled with ln -s sites-available/urlsl.me sites-enabled/urlsl.me

upstream urlslLoadBalance {
        server localhost:someport;
}

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;

        server_name urlsl.me urlshortlink.me;

        location / {
                proxy_pass      http://urlslLoadBalance;
                # some more proxy headers....
        }
}

Here's something interesting about nginx: you can put multiple servers in the upstream block and it will load balance automagically. You just have to specify the upstream in proxy_pass.

Then to make sure I didn't make any errors I used nginx -t to test the config.

I edited my DNS records to point to the new server and finally, used certbot to obtain new certificates with

sudo certbot --nginx -d urlsl.me -d urlshortlink.me

sudo service nginx restart

At this point the page was live.

https://i.imgur.com/r5cci4w.png

Note the padlock in the address bar. This means my certificates are working correctly.

See you next week!