We make a home VPS for test and pet projects


1. Remote access.

The first thing to start with is to provide an opportunity for someone from the outside world to reach your router. It is obviously a bad idea to contact using a gray IP, so we need some kind of domain name by which we can contact and get into our network. There are several options, each with its own advantages and disadvantages.

  • We use DynDNS. On the plus side, you get the opportunity to reach the router by name and solve the problem with dynamic IP. The downside is that it’s not a fact that your IP is not grayed out behind NAT, so this option may not work. Well, not all routers can use DDNS services, so linking something like myname.homeip.net to a router can be a problem.

  • Get a dedicated IP address from your provider and buy a domain. On the plus side, you will receive a beautiful link and a guarantee that everything will work, you just need to set everything up on any router. The downside is that you will have to pay to rent the address and name. True, it costs mere pennies, many registrars give a second-level domain in the ru zone for 100 rubles for the first year (and then you don’t have to renew), a fixed IP service costs about the same (or is completely free).

Mikrotik provides DNS in two clicks.  Be like Mikrotik

Mikrotik provides DNS in two clicks. Be like Mikrotik

I would also like to note that some routers have their own DDNS service and you can get a domain name in one click, although it will be very long. Therefore, I advise you not to regret 200 rubles and buy yourself a domain name and take an IP address from your provider. After that, go to the registrar’s control panel, find domain zone management and add A entry: symbol ‘@‘ as a subdomain and enter the issued address. After some time, we will get to our router using this domain.

2. Ports.

Now we need to solve the following question: how to get on request not to the router, but to our computer. And there is a way out, and it’s simple – Port forwarding. We agree with the router that all incoming requests from outside to a certain port are redirected to the address specified on the internal network. There is nothing complicated here either, we google the router model + port forwarding and get a ready-made guide.

On my Mikrotik this is done with one command. With this command we specified a NAT rule: to translate requests coming on port 80 on ether5 to IP 192.168.88.52 and port 9000. It is logical that this IP should be reserved and this is the local address of our computer.

/ip firewall nat add action=dst-nat chain=dstnat dst-port=80 in-interface=ether5 log=yes protocol=tcp to-addresses=192.168.88.52 to-ports=9000

Now we can check that everything worked. To do this, just create a dockerfile with nginx, where we copy the HTML file. In my case these are files from the Catalog folder

FROM nginx:latest
COPY ./Catalog/ /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

We assemble the image and launch the container, not forgetting to connect it to port 9000, which we forwarded to our machine a little earlier.

docker build -t catalog .
docker run --name catalog -d -p 9000:80 catalog 

Now we can check that everything went well. We can see the contents of the page that we packaged in the Nginx container. It should be available at http://localhost:9000, as well as by existing domain name. In my case, I packed the Williams publishing house catalog as of 2005. There are not many visual differences with the current version of the site, but the prices…

Actually, the main part of the task is completed – we have linked our router to the required domain name and forwarded ports to the port we need on a specific machine. Now everything that we run on port 9000 on our PC will be available, we can run any test tasks and APIs, we have access to them. But it’s too early to stop there, there is still room for improvement, so let’s move on to the next stage.

3. Containerization.

Of course, if we need to run one simple application, then there doesn’t seem to be much point in messing around with Docker, images and containers, because we can simply launch the application through the terminal or IDE directly on the specified port. In .NET you can edit launchSettings.json

{
    "applicationUrl": "http://localhost:5290",
  }

But what if we need some external systems in our task? Perhaps we are making an application that works with a database, or with a message broker, or do we even have two applications that need to work with each other? The best way would be to run them in an environment isolated from the operating system, using docker compose. Let’s create an empty webapp project and call it First. Let’s add the following dockerfile to the project

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /app

COPY *.csproj ./
RUN dotnet restore

COPY . ./
RUN dotnet publish -c release -o /app

FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /app
COPY --from=build /app ./
ENTRYPOINT ["dotnet", "First.dll"]

Let’s create another deployments folder, inside which we will place the docker-compose.yaml file. Its only task is to simply “simulate” the situation that something else needs to be launched in addition to the required application. In our case, these are Postgres and Grafana.

version: '3.8'
services:
  app:
    build:
      context: ../First
      dockerfile: ./dockerfile
    ports:
      -9000:80   # порт, на который будет роутер перенаправлять запросы
    depends_on:
      - postgres
    volumes:
      - $APPDATA/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
    restart: unless-stopped
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    networks:
      - mynet
    container_name: first
    
  grafana:
    image: grafana/grafana
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - ./monitoring/grafana-data/data:/var/lib/grafana
    networks:
      - mynet

  postgres:
    image: postgres:latest
    container_name: postgres
    restart: unless-stopped
    ports:
      - '5435:5432'
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=Pass!!w0rd""
      - POSTGRES_DB=ExampleDB
    command:
      - "postgres"
      - "-c"
      - "wal_level=logical"
      - "max_prepared_transactions=10"
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - mynet

networks:
  mynet:

volumes:
  postgres-data:
  

After the call, we will assemble the container, and our application will become available locally on port 9000, which means behind the router it will be the same port 80.

 docker compose --project-name=first -f ./deployments/docker-compose.yaml up --build -
d

Mission Complete? In part, yes, we can provide convenient access from our application to the outside world and make sure from the logs that in the end no one looked at it – what else is needed for complete happiness?

4. Subdomains.

Since such situations are not isolated, you have to take them in quantity, so the following scenario is possible: you made two test ones and sent each of them for verification. It is logical that you would want to provide the opportunity to run each of them for demonstration, but what to do, since we only connected one port?

There is a logical answer to the question – throw another one, this time not 80, but any other. Accordingly, your solution will be available at the link domain.ru:777. Although we are not averse to raising money, we are still not developing an API for casinos, which means this decision is not a great solution. Well, guided by the wisdom “Don’t look for the easy way, look for the right one,” we begin to fence our bicycles further.

We go to the registrar’s personal account and edit the domain resource records. We need to add a couple of A records and link them to our address, but now instead of the “@” subdomain we indicate the desired subdomain. I added two – first and second for clarity and simplicity. Now we need to configure routing so that requests with different subdomains are sent to different ports. The easiest way is to raise Nginx on port 9000, which will resolve incoming requests and return 404 if the request does not contain a subdomain. We create nginx.conf

events {
}

http {

server {
        listen 80;
        server_name domain.ru;

        location / {
            return 404;
        }
    }

server {
    listen 80;

    server_name first.domain.ru;

    location / {
        proxy_pass http://192.168.88.52:11000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;

    server_name second.domain.ru;

    location / {
        proxy_pass http://192.168.88.52:12000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
server {
    listen 80;

    server_name bonus.domain.ru;

    location / {
        proxy_pass http://192.168.88.52:10000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
}

And create a container

docker build -t nginx-container .
docker run -p 9000:80 --name=nginx -d nginx-container

Now all requests coming to port 9000 will be redirected from the container with Nginx over the local network: from subdomain first to port 11000, and from subdomain second at 12000. Now all that remains is to launch applications on the specified ports and check their functionality. For the first application in docker-compose.yaml you need to change the port to

ports:
      -11000:80

We can create the same empty Second WebApp application and copy the same dockerfile and docker-compose.yaml there, changing only the project name and port to 12000. The following route appears: router:80 -> our_pc:9000 -> nginx -> our_pc:11000 if the request came to first.domain.ru or our_pc:12000 if the request came to second.domain.ru. Since launched applications can be hung on (almost) any ports, just as many subdomains can be created, this should cover the basic needs for launching and demonstrating some simple projects, even at the same time.

I hope that this article will be useful to developers who are wondering about launching several projects on a PC and organizing access to it. Perhaps someone noticed the third bonus subdomain. There The full catalog of the publishing house from the past, found on an old disk, is posted. You can call and feel nostalgic. And you can join my little community in telegram.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *