You can host multiple websites on a single server in a way that is incredibly profitable for your business. And I'm going to show you how step by step, even if you never set up a server before.
We'll start with the cheapest way possible by launching a raw Linux VPS where we will set up Nginx server blocks, isolate our website databases, and launch multiple live domains for just $6 a month.
But if your goal is to build a $5,000 per month agency, I'll show you another option that might cost you more, but completely automates all of this with a click of a button and more importantly provides premium managed server features that allow you to charge each of your clients hundreds of dollars extra every single month, skyrocketing your revenue and freeing up hours of your time.
Now, like I said, there are a bunch of steps for the $6 VPS option, but before we even touch the command line, let's look at the actual math of running a modern digital agency. If you host 20 client websites on a $6 raw VPS and charge a standard $100 a month retainer, your expenses are practically zero, netting you nearly $2,000 a month in profit.
That's a decent side hustle. But look what happens when we switch to a managed VPS model. Yes, your monthly infrastructure costs increase, but because you gain access to premium automated features, you can easily upsell your clients into a website care plan. Charging $250 a month for those same 20 clients scales your business to $5,000 a month in recurring revenue, and it only goes up from there.
To help you test both of these business models out risk-free, this video is sponsored by Cloudways. I have my affiliate links down below, which if you choose to use them, I will earn a commission at no extra cost to you, which supports the channel. So, thank you. Okay, so if you're a developer who loves the command line, let's start with the manual way. And if you're an agency owner looking to scale faster without coding, skip way ahead to this timestamp for the automated managed VPS tutorial.
All right, so let's get coding. Go ahead and grab your free Digital Ocean credits linked below in the description and meet me in your Digital Ocean dashboard. First thing you want to do is click on create droplet. And then to set up our server, choose the data center that's closest to your clients. For the operating system, choose Ubuntu 24.04 long-term support. And for the droplet plan, which is your VPS in this case, you can choose any one of these.
The whole point of this video is to prove that you can host multiple websites on a very small server. So, we will choose a basic regular SSD $6 a month plan, which is one CPU core, 1 GB of RAM, and 25 GB of disk space. But as your agency grows, uh I don't recommend this setup. You're going to want to add more RAM, more CPU cores, and maybe even upgrade to a premium solid state drive storage. Okay. And then down here, we want to actually add an SSH key. So, you can set up a server with a password. I don't recommend it. It's not secure. So, in this tutorial, we're going to do things the right way. We're going to add an SSH key. And Digital Ocean is really good about this. I'm on a Mac, so I'm going to open up a terminal and run this command.
So, you could go up here into your Spotlight search and type in terminal. Hit enter. We'll make that bigger. And then we'll paste that in. So, what this is going to do is create a public private key pair uh at this directory on your system. So, go ahead and hit enter. Let's try that again. Enter. And if you want to add a passphrase, go ahead. Otherwise, keep it blank. And now at that directory right here, we have a public key. So in order to copy and paste that into this box right here, it gives you this handy dandy command where we can execute that. All right. Now, that public key is on our clipboard. So all you have to do is paste that in here. And then give your SSH key a name. Uh I'm on my MacBook Pro, so I'll just type MacBook Pro and we'll add that SSH key. Okay. So make sure that is selected.
And then coming on down here, there's nothing else we want to select other than you can optionally give your droplet a name. And then finally, click on create droplet. All right. And just like 30 seconds later, it says our virtual private server is active. We have this public IP address, IP version 4. So what you want to do is copy that. And now for each one of your clients, you want to if you manage their domain names directly or ask them to do this, you want to go into their DNS records for their domain name.
In this tutorial, we're going to use two domain names, jarlanta.com and tontravels.com. So, we'll click into there. This will be a very similar process if you're using GoDaddy or Namecheep or something like that. You just want to find the DNS records. And what we want to do is add an A record that points to the root directory for that IP address. And if you're using Cloudflare, make sure it's not proxied. Go ahead and save that. And we're going to add another record. This time it's a CNAME record. The name will be www. The target will be the domain name. And make sure you turn that proxy off. Click save. And then basically we want to repeat this process for every single one of your clients domain names. So we got this one done. Let's go ahead and do the other one for this tutorial. Okay. So that should be it for DNS records and domain names.
Let's go ahead and exit out of here. We're going to open up our terminal window again and log into the server via SSH. So we can do SSH root, which is the default user at the IP address. So go ahead and paste that. Hit enter. Are you sure you want to continue connecting? Type yes. Enter. All right. And now we are connected to that virtual private server in the remote data center in New York in my case. And now because we chose such a small server with just 1 GB of RAM in order to successfully host multiple client websites on this, we have to uh basically increase our RAM without buying new hardware. It's keeping it at $6 a month.
And the best way to do that is with a swap file. So, basically, we're going to create a swap file on the SSD drive, which expands the memory from 1 GB to an additional 2 GB for a total of 3 GB. And let's see what we're working with right now with the htop command. Uh, right now we have our memory at just about 1 GB. The swap is empty. Um, so let's go ahead and create that swap file. By the way, all of these commands are on my blog at Tony Teaches Tech. I'll have it linked down below. So, uh, in order, rather than typing them out, uh, you can just come in here, copy and paste them as we'll do in this video. So, first we'll create the blank swap file, lock down the permissions, and then tell Linux to use this as memory space. We'll enable the swap file, and then we'll make sure that we keep the swap file on even after the server reboots. So, hit enter.
And now, if we look at htop again, we will see that we have enabled a 2 GB swap file. Perfect. Now, like I said, we are using the root user that is the default user, but not a good practice. So, what we want to do is create a new user. My user is going to be called Tony. Give him a password. Confirm it. And then you, this is all optional. Go ahead and fill out what you want. Hit enter. Yes, it's correct. All right. So, we created the Tony user. We want to add him to the sudo group so he has those elevator privileges. And then, this is a little confusing for most people. Uh, right now, we created a SSH key in the beginning of the tutorial. uh under the the root user, we connect it with the root user, but we want to actually allow Tony to connect to the server with that same SSH key. So, all we're going to do is copy that from the roots directory into the new users directory. And we just want to make sure Tony owns that since it was previously from the root user.
Now, we can switch over to the Tony user. And the next thing you want to do is to lock down SSH to disallow root from even logging in and disallow password login. So, that way you can only log in with that public key that you created. So, in order to do that, we'll use the nano text editor to edit this file right here, the SSHD_config. Go ahead and type your password. And there's three items in here that we want to edit. The first is permit root login. By default, it might say yes for you. What we want to do is change that to no. Public key authentication. It's commented out. We want that to explicitly say yes. And then finally, password authentication right here. Uh, we want that to be no. All right. All right. To save this file, we can do Ctrl X Y enter. And now, because we did make changes to this file, we want to test out that those changes didn't break anything. So, we can do sudo sshd for test. And because it gave us no output, that means it's good to go. So, we can restart the process to apply those changes. And we can do that with sudo systemctl restart ssh. All right.
Now, just to make sure that we don't lock ourselves out of the system, let's open up a new tab or a new terminal window, I should say, and let's try to log in with the Tony user this time. So, SSH Tony at the IP address. Hit enter. And we are logged in right away without any problem. We can exit out of that. And just to confirm that root cannot log in again, we can try this with root and you should get the permission denied public key. All right, so let's exit out of that. Next up, let's lock down our system with a firewall. And we can use the UFW firewall on Ubuntu, which stands for uncomplicated firewall. So, basically, we want to close down a bunch of ports. UFW default deny incoming, which closes down all incoming ports. And then we want to allow all outgoing ports like this. And then the only three ports that we want to enable are SSH, which is port 22, port 80, which is the HTTP port specifically for TCP, and then port 443, which is the HTTPS again, which is specific to TCP. Now, let's go ahead and enable those firewall rules. And then let's check the status of the firewall. And this looks good to me.
We're allowing port 22, 80, and 443 for both IP version 4 and IP version 6. One other security measure that we can take is to install something called fail2ban. Let's do an update first and apt update. And then we can do apt install fail2ban. And what this program does is it basically if it sees multiple attempts to log into your server for SSH, for example, it'll go ahead and block that IP address. All right, so that's installed. So let's go ahead and start the service and make sure it starts on boot up with this command. So go ahead and hit enter. Next up, what we want to do is to make sure we automatically install security patches to the Ubuntu operating system. Uh, and the best way you can do that is with the unattended upgrades option. So, let's go ahead and execute that. Looks like we already have that installed. So, let's go ahead and configure that with this command.
And basically what this is saying is, do you want to download and install those stable updates? We do. So, go ahead and hit enter. Okay. So, I think it's about time to do a proper apt update and apt upgrade. So, we did the app update before, which makes sure we're looking at the latest packages. And the apt upgrade actually goes ahead and updates those packages. So, this will take a little bit. I'll catch up with you when it finishes. You might get this popup because we did modify that SSHD config file. What we want to do is just keep the local version that we modified. So, just hit enter. All right, cool. Now that our operating system is up to date, what we want to do is install something called a LEMP server. L EMP. L for Linux, which in this case we're using Ubuntu. E is Nginx, even though Nginx starts with an N. M for MySQL. We're going to be using a version of MySQL called Maria Database. And P for PHP, which is the programming language that allows us to generate web pages.
Specifically, we need that for WordPress, which is built upon PHP. So, we're going to do all of that. First, let's set up our infrastructure. There's a bunch of packages that we need. Like I said, Nginx, MySQL, a bunch of PHP stuff, and then Certbot, which is going to be something that we're going to be using in just a bit to issue free SSL certificates for HTTPS. So, let's go ahead and install that. Okay, nice. That's all installed. I like to check the status of Nginx just to make sure it's active and running, which it looks like it is. Okay, so with that in place, it is now time to actually go ahead and set up the individual websites for each one of our clients. Like I said, we're going to be working with two websites in this tutorial. So everything that we do, we're going to do twice. So for databases, two databases, two website directories, two users that run. And let's start with the users actually. Uh we don't want to run our websites under the Tony user.
What we actually want to do is to create a system user. It's a kind of like a background user. They can't actually log in via SSH. The sole purpose is just to isolate um everything, the privileges, all the files into that one user. Uh and that home directory is going to be in the websites subdirectory at var www which is the standard uh for Linux system. So go ahead and do that for your first website and then you can do the exact same thing for your second website. So now we have a Tony Travels user and a Jarlanta user and what we want to do next is add the www data user to the Tony Travels group. Uh this user right here is the Nginx user. Uh we need that for permissions later on and we'll do that for our next website too. And let's go ahead and make the root directory where the website files will eventually live. So we'll do that for the first website and the second website.
Next up, we'll work on the databases. Before we actually do that though, we want to run the Maria database secure installation. So basically for all this uh unit socket authentication, capital Y means that's the default. So we'll go ahead and hit enter. You don't have to type the Y. Do you want to change the root password? It says we already have the root account protected. So you can safely answer no. Hit enter. Remove anonymous users. Yes. Hit enter. Disallow root login remotely. Yes. Remove test databases. Yes. Reload the privilege tables now. Yes. Okay. Now we can log into the MySQL shell which is going to allow us to create those databases. And again I have these commands on my website. So we can go ahead over there and copy and paste them.
Let's do this all in one big execution. So basically what we're doing is we're creating a database for the first website. We're creating a user with this password and then we're going to associate that user with that database and then we're going to do that for the second website. Create the user, associate with the database and then we're going to flush the privileges and exit. Okay. So our databases are isolated. We have separate web root directories for each website. Now, as far as PHP is concerned, we don't want to run all of our website code through one single PHP process. We want to make a individual process for each website. So, uh before we do that, let's check what version of PHP you have installed. It might be different for you depending on when you're watching this video. So, we have PHP version 8.3.
So basically for each website we want to create a new file that will specify a socket where the PHP process will run from. Uh so in this case we're going to create it in the PHP directory uh subdirectory 8.3 which is the version fpm, pool.d, tonytravels.com. Hit enter and then go ahead and paste this all in. So uh it's specifying this website the user the group. Here's our socket. It's going to live at this directory. Our Nginx user and group. And then if it needs to, it can spin up at most three children. And then this is a critical one for WordPress. Uh this is allowing us to upload files up to 64 megabytes in size. Crl X Y enter. And then we'll do that same thing for our second website. Paste that in there. X Y enter. Okay. So let's go ahead and restart the PHP process manager with this command. Okay. So as far as the web server is concerned, like I said, we're going to be using Nginx.
We want to create a configuration file for each website which is going to bring everything together. So let's use the nano text editor again and we'll create a configuration file under the Nginx sites available directory for our first website and then we'll paste this in. And there it kind of is a bit overwhelming if this is the first time you're seeing Nginx but I'll try to walk you through it. So basically we're listening on port 80 for uh HTTP connections. And if it is having a tonyravels.com or www. tonytravels.com header, uh we're going to go into this server block and serve everything out of our website root directory which is right here. Uh basically the the index file which is if there's no subdirectory under a website's path, it's going to try to load one of these files in that order.
We do want to set up custom logging at these directories for Nginx if there's an error like a 404, 503 error. And then your access log is here too. Again, we're going to allow 64 megabyte uploads. And then this is your standard location block for WordPress. We do want to make sure that any PHP files go through this specific website's socket, which we just set up in that other configuration file for PHP FPM. And then we'll do some basic caching for static assets. These assets will be cached for just one year. And then finally, we'll attempt to block access to files that we don't want automated bots or malicious users to access. So that looks good. We'll do Ctrl X Y enter. And we'll do the same exact thing for our second website. Paste that in. Okay, that looks good. Ctrl X Y.
Hit enter. And now basically we created these two configuration files at the sites available directory. And the way Nginx works is there's basically the sites available directory which is this one here and then the sites enabled which is any files under here are actually published. So the conventional way to publish these is to create a symbolic link from the sites available directory to the sites enabled directory. Think of it like a shortcut on Windows. And we can do that really easily with the ln command. So like I said linking from sites available to the sites enabled directory.
We'll do that for each one of our websites. And let's make sure our configuration is valid. So we can do that with sudo nginx -t. And it says our syntax is okay. And the test is successful. So that means we can restart the Nginx process to apply those changes. Go ahead and hit enter. Cool. So at this point, we can actually do a very basic test. Let's create a simple HTML file. We'll use the nano text editor and we'll put that in the root of our HTML directory for each website.
So that's what it's going to look like for the first one. And we'll do that same thing for the second website and save that. All right, let's minimize this and open up a new tab. And actually, what we're going to do is open up a guest window or private window and test these out. So, tonytravels.com. That loads our test html. And then we'll do jarlanta.com. That looks good, too. Now, the one problem is that these websites are not secure.
As you can see here, and the reason is because we have not enabled HTTPS yet, uh, HTTPS basically allows these websites to go across the internet from your server through an encrypted connection. So let's go ahead and do that next. I alluded to this earlier. We can do this with Certbot and Let's Encrypt which is a free SSL certificate for all of your websites as many as you need. And we can do that with the Certbot command. Uh since we're using Nginx, we'll pass the Nginx flag. And then our domains are right here listed out individually, both www and non-www versions of our domain. So let's go ahead and do that to get our SSL certificates. Go ahead and give it an email address for certificate renewals and then agree to the terms.
You don't have to sign up for that. Hit enter. And that's going to go ahead and request those three SSL certificates for us. All right, it looks like it has successfully deployed those certificates. And the one thing I'd like to do after I install an SSL certificate with Let's Encrypt is to go back into the etc Nginx sites available directory and let's just look at one of our website configuration files. Uh before um we had all this information in here but basically what it does is it comes in here and adds these extra server blocks for um listening on port 443 which is HTTPS port and it points to those SSL certificates.
Not only that, but it also does a redirect from, uh, www to non-www, a 301 permanent redirect. And also what this does is make sure that HTTP is redirected to HTTPS. So that looks good. We'll go ahead and get out of that. And now we can test to see if we have a secure connection. So just refresh those two tabs that you have open. And this time we see that we have HTTPS for both of our websites. All right, let's go ahead and open your command window up yet again. Uh, before you install WordPress, there are some PHP packages that we need to install that WordPress depends on. So, you can go ahead and copy that command from my website and execute it. All right.
And then for good measure, let's go ahead and restart the PHP process manager like we did before. And those changes should take effect. Now, it is time to install WordPress. First, we have to get the latest version of WordPress from uh their website. So, let's actually put that into a temporary directory. And we'll use the wget command to pull that down. Hit enter. And now we have that in here, right here. So, let's use the tar program to extract that. And it's extracting into the WordPress directory right here. And let's see what that looks like. So, we have a bunch of files, our index.php. An important one is the WP content which has the all your plugins and themes and whatnot.
So that looks good. Now what we want to do is basically copy everything underneath here into our each of our clients websites. Uh we do have that test file that we want to get rid of first. So we can just remove that with the rm command. And we'll do that for the other website too. We don't need those anymore. And then let's copy all of the files under the WordPress directory into each one of our clients websites. And then let's make sure that we have the proper permissions with the chown command, the chown command, uh to make sure that each website directory is associated with the correct user in the correct group. And we'll do that for our second website, too. All right. Now, back in our web browser, we can refresh the page and we should see the WordPress setup configuration.
So, basically, what we have to do is supply those database usernames and passwords that we created a little while ago. Um, let's go ahead and do that. So for this one, we had this user, this database, our password, and everything else could remain the same. So we'll hit submit. Ah, it looks like I switched these two. So this should be database name, Tony Travels DB, and then Tony Travels user. Let's try that. All right, that looks good. So we'll run the installation, give it a site title, username, password, email, and then install WordPress. And we'll do that for the other website. Now we can log into each one of our websites with the credentials that we just created. Log in. And these are our WordPress admin dashboards for each one of our website. It just has the default theme at this point. And we can see what that looks like by going to the homepage.
Now, there's one last thing we have to do to lock down our WordPress installation, and that is just a bunch of permissions to make sure that uh because we copied a bunch of files, um we just want to make sure that everything's locked down appropriately. So yeah, back on my blog, we want to make sure that all files have the 755 permission for both of our websites. And then for the directories under there, we want the 644. And then finally, our wp-config.php file. We'll make that a 640 for each one of our websites. All right. And there you go. We have both of our websites on a single server.
One of the best ways to test that out is with the ping command. So ping tonytravels.com. We have this IP address which is our Digital Ocean IP address. We should get back the same one for Jarlanta and that means that they are both being hosted on the same server. All right, welcome to the fast lane. Now, if you skipped ahead, it means you value your time as an agency and you understand how a managed VPS allows you to charge your clients more, which take a look at the breakdown on your screen right now to see the exact differences. While both of these options are fully production ready, you just get a whole lot more with Cloudways without touching a single piece of code.
So, let's jump right into the Cloudways dashboard. We'll go ahead and click add application to start the setup. In this first drop down under the application, we're just going to select WordPress. Then, you can give your application a name, your server a name, and optionally organize into different projects. For the application stack, let's go with the lightning stack, which is preconfigured with Nginx caching for speed. And for the server infrastructure, we're going to choose Digital Ocean.
For the database, select Maria database. And for the server type, choose basic for the most cost effective configuration. Then we can use this slider right here to choose our server size. I'm going to keep mine on this 4 GB RAM tier because we want plenty of headroom to safely host a bunch of client websites on this single machine. Finally, pick the data center closest to your clients and then we'll click launch now. It'll take about 5 or 10 minutes for the system to spin everything up and optimize it in the background. So, I'll catch up with you when that's finished. Okay, now that our server is up and running, our first WordPress site is already live, as you can see here.
But remember, the secret to making this business model highly profitable is hosting multiple clients on the single machine, which is as easy as clicking on add application, selecting the server you just created, and choosing WordPress again for your next client's website. Each website runs in its own isolated environment. So, if one of your client sites ever has a security issue, it's completely locked down and won't affect any of your other clients on the server. Now, by default, you get a temporary staging URL, as you can see here. But when you're ready to actually launch the site for your client, mapping their real domain name is pretty simple. You just go to domain management, type in their domain name, and make it the primary domain.
Then, inside your domain register, and we're using Cloudflare today, you just want to point an A record to your server's IP address for both the root domain and the www version. Just make sure that orange proxy toggle is turned off for now. And I'll explain why we're doing that in an upcoming step. To secure the site, you just hop down to the SSL certificate tab right here. Enter your email, the domain name, the www version of your domain, and click on install certificate. It sets up a free SSL Let's Encrypt certificate, and handles all the renewals automatically in the background. So, the site stays completely secure indefinitely. Now, at this point, we have pretty much the same exact hosting setup as we had previously done in the beginning of this video with a raw Linux VPS setup. And everything that we enable from this point forward is how you can confidently justify a $250 per month website care plan to your clients because these tools protect their business while saving you a ton of time and manual effort.
First, we're going to turn on site manager pro, which gives you access to a feature called safe updates. Instead of just turning on basic auto updates and praying that a plugin doesn't break a client's layout, this tool clones the website into a safe staging area, runs the updates and uses an AI visual check to make sure everything looks perfect. If it passes, it updates the live site. If it fails, it rolls back instantly and alerts you. Second, let's turn on Cloudflare Enterprise. Now, you might remember that free orange switch we saw during the DNS setup. That free version is totally fine for a basic personal blog, but true enterprise plans usually cost thousands of dollars a month if you buy them directly from Cloudflare.
Thankfully, we get the same enterprise features for really cheap, which includes a faster CDN, a much stronger WordPress firewall, and edge caching, so most of your traffic doesn't even touch your actual server. Plus, with 20 clients, the price drops to just $3 per month per site. So, it's an easy way to give your clients serious security without hurting your margins. And honestly, it's a huge selling point when you're selling to new clients. Similarly, you'll want to set up automated daily off-site backups to reassure your clients. And this is done at the server level. Cloudways handles this completely on autopilot by backing up your entire business's data to an external server. And you can also enable local backups for additional peace of mind.
Now, for the situation when a client reaches out to you and wants to test a new plugin or completely redesign a landing page, you never want to do that on their live site. With just one click, you can spin up a private staging copy of their site, do all your development work safely behind the scenes, and then push it live only after the client approves it. Now, both of these business models are highly profitable. If you're a developer who wants to master Linux system administration for just six bucks a month, then the manual VPS option is perfect for you.
But if your goal is to scale up a digital agency and build a recurring revenue stream, then the managed VPS option is the clear winner. Now, if you want to try out either one of these options risk free today, use my affiliate links in the description below. Thank you guys for watching. I'll see you in the next


No comments:
Post a Comment