How I setup a $1/year VPS for web hosting

During this past Black Friday sales event, I was able to pick up a $1/year vps from Gullo’s Hosting with a promo code. (You can no longer get them for that cheap. Now they are available for $3.5/year) The specs for this VPS are very limiting:


  • NAT IPV4 (20 ports)
  • IPV6/80
  • 128MB RAM
  • 1 vCore
  • 3GB HDD
  • 125GB BW

The most limiting factor for this server is going to be RAM, which I quickly found out after I had Centos 7 pre-installed. The package manager, yum, could not install any server packages such as PHP, or Nginx. It would run out of ram. I didn’t want to revert to a much older distro because I wanted to also use the latest packages and continue to get updates. There is one distro I found which is up to date and works flawlessly on 128MB of ram: Alpine Linux.


Now I needed to get Alpine onto my VPS. You can’t get it pre-installed in most cases so you’ll have to side-load it by erasing everything in / and replacing it with an Alpine base system. Luckily this is very easy if you use an existing script which is available here. You only need to copy that script to your disk and then execute it. It will automatically erase everything and replace your distro with Alpine Linux. This worked flawlessly on Centos 7. Also, before you erase everything you may want to check on your IPv6 settings since you will need to set those again manually after reboot.


Once you’re in your new Alpine Linux VPS, there are a few things you may need to do to get it fully working.If you notice there are a lot of error messages in /var/log/messages about /etc/inittab then you should try:

apk add e2fsprogs-extra
chattr +i /etc/inittab

I don’t know what that does, but it makes the errors go away. Next you’ll need to configure your IPv6, which you will need to use when you configure a reverse proxy for web hosting. Open up /etc/network/interfaces and add the following lines:

iface venet0 inet6 static
address abcd:123:456:7890:ef::abcd
netmask 80
gateway abcd:123:456:7890:ef::1
pre-up echo 0 > /proc/sys/net/ipv6/conf/venet0/accept_ra

Remember to change the bold areas to what’s relevant to you. Your hosting provider should give you what IPV6s you have, you need only pick one for yourself. Reboot or restart networking. Make sure you are able to ping your IPv6 address. If all is working then you may setup your hosting. I prefer Nginx because it’s fast, scales relatively well, and doesn’t use too much ram. With only 128MB of ram that limit is going to be hit if enough people are using your server, the goal here is to keep the server working under normal load. And to use an aggressive caching policy so the server can even handle usage spikes.


You can follow the guide to installing nginx and php on Alpine Linux. I went with PHP7. You’ll definitely want to lower your PHP_MEMORY_LIMIT, I used 64MB. I am not going to install mysql because that takes far too much memory. Instead, for any database needs I use another offsite mysql server. Because this will increase the time it takes to deliver pages dramatically, I need to enable aggressive caching. Those changes (and some other unrelated preferences I have) are to the nginx.conf file. this is commented nginx.conf I use.

#You might need to change this to whatever you configured
user                            www;
worker_processes                auto; # it will be determinate automatically by the number of core

error_log                       /var/log/nginx/error.log warn;

events {
    worker_connections          1024;
}

http {
    include                     /etc/nginx/mime.types;
    default_type                application/octet-stream;
    sendfile                    on;
    access_log                  /var/log/nginx/access.log;
    keepalive_timeout           3000;
	#This sets the cache path, 32m is the max size of the cache, 20160minutes is two weeks
	#If a cached file is not fetched within two weeks it is deleted
	#Only use this for static websites that change very infrequently
    fastcgi_cache_path /etc/nginx/cache levels=1:2 keys_zone=MYAPP:32m inactive=20160m;
    fastcgi_cache_key "$scheme$request_method$host$request_uri";
    server {
        #IPV6
        listen                  [::]:80;
        root                    /var/www/yourwebsite/dir;
        index                   index.html index.htm index.php;
        server_name             yourdomain.com www.yourdomain.com;
        client_max_body_size    32m;
        error_page              500 502 503 504  /50x.html;
		#This sets it so some files/directories do not get cached
        set $no_cache 0;
		#All requests with post data do not get cached as they likely
		#need up to date information
        if ($request_method = POST)
        {
            set $no_cache 1;
        }
		#If you're using wordpress or something, you probably don't want
		#The admin area cached.
        if ($request_uri ~* "/(wp-admin/)")
        {
            set $no_cache 1;
        }
		#sets a header so we can see if a request is cached or not
        add_header X-Cache $upstream_cache_status;
        location / {
            try_files $uri/index.html $uri $uri/;
        }
        location = /50x.html {
              root              /var/lib/nginx/html;
        }
		#php configuration
		location ~ \.php$ {
              fastcgi_pass      127.0.0.1:9000;
              fastcgi_index     index.php;
              include           fastcgi.conf;
			  #where we selectively disable caching based on
			  #if statements above
              fastcgi_cache_bypass $no_cache;
              fastcgi_no_cache $no_cache;
              fastcgi_cache MYAPP;
			  #200 means only cache 200 response OK files
			  #20160m means files will be cached for 2 weeks
              fastcgi_cache_valid 200 20160m;
        }
        location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
				#images, css, and javascript will stay on
				#the end user's browser cache for a year
                expires 365d;
        }
    }
}

Now that you have your hosting setup, you will need to make it accessible to the outside world. Since this server is behind a NAT and only has an IPv6, you will need to use a reverse proxy. CloudFlare provides this service free of charge. All you have to do is:

  • Make a free account with CloudFlare.
  • Tell CloudFlare what domain you want them to manage.
  • On your domain registrar, change the DNS to CloudFlare servers.
  • Configure two AAAA records on CloudFlare. one with “@” and another with “www” as the names. Both should point to your IPv6 address.
  • Remove any other A or AAA DNS records which point to a parking page or somewhere other than your server.
  • Make sure CloudFlare is on and it is configured to cache and act as a reverse proxy for your server.

You should now be able to access your web host through your domain! Another advantage of CloudFlare is they are a full CDN which will provide additional caching for your website which will increase the speed and reduce the bandwidth requirements from your host.


This is how I was able to turn a dirt cheap $1 VPS into a fairly fast and reliable website hosting machine. In fact, with hosting this cheap, by far the largest cost is the domain registration. Also I cheated a bit and didn’t use a local MYSQL server since you’re definitely going to have trouble with that on 128 megs of ram. But still, I was able to turn an extremely limited server that cost the same as a Jr. Bacon Cheeseburger in the early 2000s into something that can actually be used for my businesses.