{"id":688,"date":"2025-02-06T13:41:55","date_gmt":"2025-02-06T13:41:55","guid":{"rendered":"https:\/\/www.taticloud.com\/?page_id=688"},"modified":"2025-04-01T11:16:09","modified_gmt":"2025-04-01T11:16:09","slug":"projects-build-your-portfolio-website-with-devops","status":"publish","type":"page","link":"https:\/\/www.taticloud.com\/?page_id=688","title":{"rendered":""},"content":{"rendered":"\n<div class=\"wp-block-group alignfull has-global-padding is-layout-constrained wp-container-core-group-is-layout-3 wp-block-group-is-layout-constrained\" style=\"min-height:0px;margin-top:0px;margin-bottom:0px;padding-top:1rem;padding-right:1rem;padding-bottom:1rem;padding-left:1rem\">\n<div class=\"wp-block-group alignfull has-background has-global-padding is-layout-constrained wp-container-core-group-is-layout-2 wp-block-group-is-layout-constrained\" style=\"background-color:#e9e4dc;margin-top:0;margin-bottom:0;padding-top:7.01rem;padding-right:1.01rem;padding-bottom:7.01rem;padding-left:1.01rem\">\n<h2 class=\"wp-block-heading has-text-align-left has-text-color\" style=\"color:#3a3a3a;font-size:clamp(1.383rem, 1.383rem + ((1vw - 0.2rem) * 1.595), 2.26rem);font-style:normal;font-weight:400\"><strong>Build Your Portfolio Website with DevOps: A Scalable and Secure Approach<\/strong><\/h2>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-1 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"628\" src=\"https:\/\/www.taticloud.com\/wp-content\/uploads\/2024\/01\/nearshore_2021.11.25_social.jpg\" alt=\"\" class=\"wp-image-239\" srcset=\"https:\/\/www.taticloud.com\/wp-content\/uploads\/2024\/01\/nearshore_2021.11.25_social.jpg 1200w, https:\/\/www.taticloud.com\/wp-content\/uploads\/2024\/01\/nearshore_2021.11.25_social-300x157.jpg 300w, https:\/\/www.taticloud.com\/wp-content\/uploads\/2024\/01\/nearshore_2021.11.25_social-1024x536.jpg 1024w, https:\/\/www.taticloud.com\/wp-content\/uploads\/2024\/01\/nearshore_2021.11.25_social-768x402.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"padding-left:2.01rem\">\n<div class=\"wp-block-group is-vertical is-content-justification-left is-nowrap is-layout-flex wp-container-core-group-is-layout-1 wp-block-group-is-layout-flex\" style=\"min-height:100%\">\n<p class=\"has-text-color\" style=\"color:#3a3a3a;font-size:clamp(0.875rem, 0.875rem + ((1vw - 0.2rem) * 0.464), 1.13rem);\"><strong>Are you ready to showcase your skills as a DevOps engineer with a professional portfolio website?<\/strong> Imagine having a fully functional, cloud-hosted site that highlights your expertise and serves as a hands-on DevOps project to impress recruiters and clients. \ud83d\ude80<\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:#3a3a3a;font-size:clamp(0.875rem, 0.875rem + ((1vw - 0.2rem) * 0.464), 1.13rem);\">Through this project, I successfully built and deployed a portfolio website using <strong>Linux, Apache, MySQL, and PHP (LAMP stack)<\/strong>\u2014an essential combination for web hosting. <\/p>\n\n\n\n<p class=\"has-text-color\" style=\"color:#3a3a3a;font-size:clamp(0.875rem, 0.875rem + ((1vw - 0.2rem) * 0.464), 1.13rem);\">This journey was part of the ZTM DevOps Bootcamp, where I learned to apply industry-standard tools to create a scalable, secure, and professional portfolio site. Whether you\u2019re an aspiring DevOps engineer, a developer, or a tech enthusiast, this project will equip you with practical cloud infrastructure skills that are highly valuable in the industry.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n\n\n\n<h1 class=\"wp-block-heading has-text-align-center has-text-color\" style=\"color:#3c3123;font-size:clamp(1.502rem, 1.502rem + ((1vw - 0.2rem) * 1.815), 2.5rem);\"><\/h1>\n<\/div>\n\n\n\n<div class=\"wp-block-group alignfull has-background has-global-padding is-layout-constrained wp-container-core-group-is-layout-4 wp-block-group-is-layout-constrained\" style=\"background-color:#e9e4dc;margin-top:0;margin-bottom:0;padding-top:7.01rem;padding-right:1.01rem;padding-bottom:7.01rem;padding-left:1.01rem\">\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-2 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<p class=\"has-text-color\" style=\"color:#3a3a3a;font-size:clamp(1.383rem, 1.383rem + ((1vw - 0.2rem) * 1.595), 2.26rem);font-style:normal;font-weight:400\"><strong><strong>Overview<\/strong><\/strong><\/p>\n\n\n\n<p>This project walks through the <strong>end-to-end process<\/strong> of setting up a <strong>cloud-based portfolio website<\/strong> from scratch. Here\u2019s what we\u2019ll cover:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1\ufe0f\u20e3 <strong>Cloud Server Setup<\/strong><\/h3>\n\n\n\n<p>\u2705 Deploy a Virtual Private Server (VPS) on DigitalOcean<br>\u2705 Configure essential firewall and security settings<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2\ufe0f\u20e3 <strong>Domain &amp; DNS Configuration<\/strong><\/h3>\n\n\n\n<p>\u2705 Register a custom domain for a professional web presence<br>\u2705 Configure BIND9 DNS Server for domain name resolution<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3\ufe0f\u20e3 <strong>Web Server Installation &amp; Configuration<\/strong><\/h3>\n\n\n\n<p>\u2705 Install and optimize Apache2, one of the most powerful web servers<br>\u2705 Enable HTTP\/HTTPS protocols and configure SSL certificates<br>\u2705 Set up virtual hosting to manage multiple websites efficiently<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4\ufe0f\u20e3 <strong>Implementing the LAMP Stack<\/strong><\/h3>\n\n\n\n<p>\u2705 Install and configure Linux, Apache, MySQL, and PHP<br>\u2705 Manage databases with MySQL<br>\u2705 Test and verify PHP functionality<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">5\ufe0f\u20e3 <strong>Installing and Securing phpMyAdmin<\/strong><\/h3>\n\n\n\n<p>\u2705 Install phpMyAdmin for database management<br>\u2705 Secure MySQL access with strong user authentication<br>\u2705 Restrict access to phpMyAdmin for added security<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6\ufe0f\u20e3 <strong>Deploying WordPress as a CMS<\/strong><\/h3>\n\n\n\n<p>\u2705 Download and install WordPress<br>\u2705 Set up a secure database for WordPress<br>\u2705 Manage WordPress settings for optimal performance and security<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">7\ufe0f\u20e3<strong> Securing the Website<\/strong><\/h3>\n\n\n\n<p>\u2705 Implement firewall configurations for protection<br>\u2705 Secure the wp-admin directory and limit login attempts<br>\u2705 Regularly update plugins and core files to prevent vulnerabilities<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">8\ufe0f\u20e3 <strong>Troubleshooting &amp; Performance Optimization<\/strong><\/h3>\n\n\n\n<p>\u2705 Identify and fix common web server issues<br>\u2705 Optimize server performance and database queries<br>\u2705 Ensure scalability for future growth<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"padding-left:2.01rem\">\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What I Achieved &amp; Future Scalability<\/strong><\/h2>\n\n\n\n<p>\u2705 A <strong>fully functional, cloud-hosted portfolio website<br><\/strong>\u2705 Hands-on experience with <strong>real-world DevOps tools and practices<br><\/strong>\u2705 A <strong>secure and scalable<\/strong> infrastructure, ready to grow with my career. With this setup, my portfolio site can <strong>scale effortlessly<\/strong>, allowing for additional <strong>custom applications, blogs, or even e-commerce integration<\/strong> in the future. By mastering these skills now, I am fully prepared for <strong>larger-scale cloud and DevOps projects<\/strong>.<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Why You Should Build This Too<\/strong>? <\/h2>\n\n\n\n<p>What if you could <strong>replicate this project<\/strong> and deploy your <strong>cloud-hosted portfolio website<\/strong>? This is <strong>not just another coding tutorial<\/strong>\u2014this is an <strong>industry-relevant DevOps project<\/strong> that gives you hands-on experience <strong>deploying real infrastructure<\/strong>.<\/p>\n\n\n\n<p>\u2705 <strong>Showcase your DevOps skills<\/strong> with a working, hosted portfolio<br>\u2705 <strong>Gain hands-on experience<\/strong> with cloud servers, web hosting, and security<br>\u2705 <strong>Enhance your resume<\/strong> with a real-world project that recruiters love<br>\u2705 <strong>Future-proof your site<\/strong> by understanding <strong>scalability and security<\/strong> best practices<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Ready to Build Your Cloud-Based Portfolio? \ud83d\ude80<\/strong><\/h2>\n\n\n\n<p>Follow along as I <strong>guide you through every step<\/strong>\u2014from setting up your <strong>server and domain<\/strong> to securing and deploying your website. Let\u2019s <strong>turn theory into practice<\/strong> and build a portfolio site that speaks for itself!<\/p>\n<\/div>\n<\/div>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"has-text-color\" style=\"color:#3a3a3a;font-size:clamp(1.383rem, 1.383rem + ((1vw - 0.2rem) * 1.595), 2.26rem);font-style:normal;font-weight:400\"><strong>Project Guide<\/strong><\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong>Step 1: Setting Up Your DigitalOcean Droplet<\/strong><br><\/summary>\n<p>To get started, you&#8217;ll need a cloud server to host your website. For this project, I chose DigitalOcean, as its Droplets (virtual private servers) are ideal for small projects like portfolio sites. Creating a Droplet is quick and simple, requiring just a few clicks on DigitalOcean\u2019s platform.<\/p>\n\n\n\n<p>When selecting your server, go for at least <strong>1GB RAM<\/strong> to prevent MySQL issues later. While I initially went with 512MB RAM, it was a bit too tight for my database needs.<\/p>\n\n\n\n<p>After your Droplet is set up, access it via SSH (don&#8217;t worry if that sounds complicated \u2013 it\u2019s just a command to log into your server):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ssh root@your_droplet_ip<\/code><\/pre>\n\n\n\n<p>Now, go to your Digitalocean account and take a snapshot of your droplet before making any major changes to ensure you can always roll back if something goes wrong.<br><\/p>\n\n\n\n<p><\/p>\n<\/details>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong>Step 2 &#8211; Getting a Domain name<\/strong><br><\/summary>\n<p>Next, it\u2019s time to get your <strong>domain name<\/strong>. I chose Namecheap for this, but you can go with any domain registrar you like. Once you have your domain (let\u2019s say, example.com), it\u2019s time to set up your <strong>DNS<\/strong> to point it to your server.<\/p>\n\n\n\n<p>To check the current nameservers for your domain, run this command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">dig -t ns example.com<\/code><\/pre>\n\n\n\n<p>This will show the nameservers provided by your registrar:<\/p>\n\n\n\n<p>dns1.registrar-servers.com<\/p>\n\n\n\n<p>dns2.registrar-servers.com<\/p>\n\n\n\n<p>If you want to set up your own DNS, you can configure a custom nameserver (like ns1.example.com). To customize your authoritative nameserver to ns1.example.com, go to your account on namecheap.com and manage your domain in Advanced DNS.<\/p>\n\n\n\n<p>This process can take up to <strong>24 hours<\/strong> to update globally.<\/p>\n\n\n\n<p><strong><em>Notice,<\/em><\/strong> that it is recommended to have two DNS servers. Update the information about the second DNS and create a second authoritative nameserver. To simplify the process and save our project budget, we only created ns1.<\/p>\n<\/details>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong>Step 3: Installing Your DNS Server (BIND9)<\/strong><\/summary>\n<p>For this project, we use <strong>BIND9<\/strong> as our DNS server. It\u2019s the go-to software for managing DNS. BIND 9 is transparent, open source, and full-featured, providing flexibility during server setup for any application.<\/p>\n\n\n\n<p>To install it on your server, run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo ssh -l root@your_droplet_ip\nsudo apt update &amp;&amp; apt install bind9 bind9utils bind9-doc<\/code><\/pre>\n\n\n\n<p>Once installed, check the status of BIND9:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo systemctl status bind9<\/code><\/pre>\n\n\n\n<p>You&#8217;ll also need to configure it to use <strong>IPv4<\/strong>. Open the configuration file and update the option. You can use any terminal-based text editor, or stick with Vim like I did in my project. You can install it with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo apt install vim<\/code><\/pre>\n\n\n\n<p><br>Open the configuration file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo vim \/etc\/default\/named<\/code><\/pre>\n\n\n\n<p>Add this line:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">OPTIONS=\"-u bind -4\"<\/code><\/pre>\n\n\n\n<p>Then, restart BIND9:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">systemctl reload-or-restart bind9<\/code><\/pre>\n\n\n\n<p>You can test whether your server is resolving DNS queries correctly by running:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">dig -t a @localhost google.com<\/code><\/pre>\n\n\n\n<p>To send a DNS query specifically for an &#8220;A&#8221; record for the domain google.com using a different IP address (in our example 1.1.1.1), you can execute the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">dig -t a @1.1.1.1 google.com<\/code><\/pre>\n\n\n\n<p>To verify whether the main configuration file was created in the \/etc\/bind directory:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">cat \/etc\/bind\/named.conf<\/code><\/pre>\n\n\n\n<p>In my project, I have added two forwarders (8.8.8.8 and 8.8.4.4) to our server. This helps reduce the number of queries to servers on the Internet and allows us to build a large cache of information on the forwarders. By doing so, we can maintain a strict separation between internal and external DNS and prevent exposing internal domains on the open Internet. DNS forwarding aims to increase network efficiency by reducing bandwidth usage and improving the speed at which DNS requests are fulfilled.<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">cat \/etc\/bind\/named.conf.options\noptions {\n    directory \"\/var\/cache\/bind\";\n    forwarders {\n        \t        8.8.8.8;\n            \t8.8.4.4;\n    };\n\n<\/code><\/pre>\n\n\n\n<p>Restart the server to apply settings:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">systemctl reload-or-restart bind9\nsystemctl status bind9<\/code><\/pre>\n\n\n\n<p>Run this command to test your server:&nbsp;<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">dig @localhost -t a parrotlinux.org<\/code><\/pre>\n<\/details>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong>Step 4: Setting Up Your Authoritative DNS Server<\/strong><\/summary>\n<p>Once BIND9 is up and running, it\u2019s time to set up your authoritative DNS server. For large volumes of inbound requests, it&#8217;s recommended to use master and slave configurations to balance loads. For now, we\u2019ll keep it simple as a master server (good for fewer than 50,000 requests a day).<\/p>\n\n\n\n<p>Access your virtual private server, and check the current status of BIND9:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo ssh -l root@your_droplet_ip\nsudo systemctl status bind9\nsudo systemctl enable bind9\nsudo systemctl start bind9<\/code><\/pre>\n\n\n\n<p>Run the next command to ensure that the BIND9 service starts automatically every time your server reboots:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo systemctl enable bind9\nsudo systemctl start bind9<\/code><\/pre>\n\n\n\n<p>Start BIND9 service:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo systemctl start bind9<\/code><\/pre>\n\n\n\n<p>Create the configuration for your domain (example.com), and set up a master zone for direct DNS resolution:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">vim \/etc\/bind\/named.conf.local<\/code><\/pre>\n\n\n\n<p>Add the master zone in the configuration file:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">zone \"example.com\"\t{\n    \ttype master;\n    \tfile \"\/etc\/bind\/db.example.com\"; # Zone file for direct resolution\n};\n\/\/ Do any local configuration here\n\/\/\n\n\/\/ Consider adding the 1918 zones here, if they are not used in your\n\/\/ organization\n\/\/include \"\/etc\/bind\/zones.rfc1918\";<\/code><\/pre>\n\n\n\n<p>A new zone, example.com, has been created as a master zone, meaning it is the master authoritative DNS server for our domain. We used a zone template file to create our zone file. Just copy the db.emty file to db.example.com file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">cp \/etc\/bind\/db.empty \/etc\/bind\/db.example.com<\/code><\/pre>\n\n\n\n<p>Go to your preferred text editor (in our project, we use the vim editor):<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">vim \/etc\/bind\/db.example.com<\/code><\/pre>\n\n\n\n<p>And edit the text:&nbsp;<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">$TTL    86400\n@    IN    SOA    ns1.example.com root.localhost. (\n   \t\t   \t1   \t ; Serial\n   \t\t  604800   \t ; Refresh\n   \t\t   86400   \t ; Retry\n   \t\t 2419200   \t ; Expire\n   \t\t   86400 )       ; Negative Cache TTL\n;\n@    IN    NS  \tns1.example.com.\n;@  \tIN  \tNS  \tns2.example.com.\nns1 \tIN  \tA   \t138.0.0.0 (IP address of DigitalOcean droplet)\n;@  \tIN  \tMX 10   your_email_address@gmail.com.\nyour_domain.com.\tIN\tA\t138.68.71.209\nwww \tIN  \tA   \t138.0.0.0 (IP address of DigitalOcean droplet)    mail\tIN  \tA   \t138.0.0.0 (IP address of DigitalOcean droplet)\nexternal\tIN\tA \t91.189.88.181\npublic-dns\tIN\tA\t8.8.8.8\n<\/code><\/pre>\n\n\n\n<p>After configuring the primary master server and creating the master zone file, we restarted BIND9 to apply the new configurations:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo systemctl restart bind9<\/code><\/pre>\n\n\n\n<p>To test DNS Server run these commands:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo dig @localhost -t ns example.com\nsudo dig @localhost -t a www.example.com<\/code><\/pre>\n\n\n\n<p>Test your DNS from another machine (you should wait 24 hours after registering DNS):<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo dig -t ns example.com\nsudo dig -t a www.example.com\nsudo dig -t a public-dns.example.com\n<\/code><\/pre>\n\n\n\n<p>Notice, if we add another subdomain, we will see it on another machine.<\/p>\n\n\n\n<p>&nbsp;Optionally, you can enable a reverse resolution to translate your IP to your domain.<\/p>\n<\/details>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong>Step 5: Installing a Web Server (Apache2)<\/strong><br><\/summary>\n<p>Now that our server was up and running, it was time to set up a web server. We had two solid choices: Apache2 or Nginx. Both are excellent, but we decided to go with Apache2 since we had covered it extensively in our course.<\/p>\n\n\n\n<p>Apache2 (often just called &#8220;Apache&#8221;) is one of the most popular web servers in the world. It\u2019s open-source, highly customizable, and trusted by millions of websites. The reason for its success? A flexible architecture, a huge support community, and a ton of documentation to help troubleshoot any issues.<\/p>\n\n\n\n<p>Installing Apache2 was a straightforward process. We updated our package lists and installed the server with a single command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo apt update &amp;&amp; apt install apache2<\/code><\/pre>\n\n\n\n<p>Once the installation was complete, we checked if Apache2 was running:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">systemctl status apache2<\/code><\/pre>\n\n\n\n<p>&nbsp;If everything was set up correctly, we should see a green \u201cactive\u201d status.<\/p>\n\n\n\n<p>To ensure Apache starts automatically whenever the server boots up, we ran:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">systemctl is-enable apache2 <\/code><\/pre>\n\n\n\n<p>If needed, we could also disable it from starting at boot time:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">systemctl disable apache2<\/code><\/pre>\n\n\n\n<p>Next, we checked whether our uncomplicated firewall (UFW) was enabled. By default, UFW is turned off on Ubuntu, but it\u2019s always good to confirm:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo ufw status<\/code><\/pre>\n\n\n\n<p>If Apache is running, you can enable both HTTP and HTTPS by running the following command:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo ufw allow 'Apache Full'<\/code><\/pre>\n\n\n\n<p>Now for the exciting part\u2014testing our installation! To verify Apache2 was working, we opened a browser and typed in our server\u2019s public IP address. If everything was set up correctly, we would see Apache&#8217;s default welcome page.<\/p>\n\n\n\n<p>To find our public IP address, we used one of these commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ip addr\ncurl -4 ident.me<\/code><\/pre>\n<\/details>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong>Step 6: Setting Up Virtual Hosting<\/strong><br><\/summary>\n<p>At first, our plan was simple: set up a single website. But after a little brainstorming, we realized that in the future, we might want to host multiple sites on this server. That\u2019s where <strong>virtual hosting<\/strong> came in.<\/p>\n\n\n\n<p>Virtual hosting allows a single server to handle multiple websites. Instead of using separate IP addresses for each site, Apache can differentiate between domains and serve the correct content based on the incoming request. There are two types:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>IP-based virtual hosting<\/strong> \u2013 Each website gets its own IP address.<\/li>\n\n\n\n<li><strong>Name-based virtual hosting<\/strong> \u2013 Multiple websites share the same IP, but Apache uses domain names to distinguish them.<\/li>\n<\/ol>\n\n\n\n<p>We chose <strong>name-based virtual hosting<\/strong> because it\u2019s easier to manage and doesn\u2019t require multiple IP addresses.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Creating a Virtual Host<\/strong><\/h4>\n\n\n\n<p>First, we created a directory for our domain:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">mkdir \/var\/www\/example.com<\/code><\/pre>\n\n\n\n<p>Then, we made sure Apache had the correct permissions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">ps -ef | grep apache2<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">chown -R www-data.www-data \/var\/www\/example.com\/<\/code><\/pre>\n\n\n\n<p>Setting permissions of the directory:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">chmod 755 \/var\/www\/example.com\/<\/code><\/pre>\n\n\n\n<p>To test our setup, we created a simple web page inside the directory:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">vim \/var\/www\/example.com\/index.html<\/code><\/pre>\n\n\n\n<p>We added the following line:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">Hello, this is example.com website!<\/code><\/pre>\n\n\n\n<p>Now, it was time to tell Apache about our new site. We created a configuration file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">vim \/etc\/apache2\/sites-available\/example_com.conf<\/code><\/pre>\n\n\n\n<p>We added the following content:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&lt;VirtualHost *:80&gt;\n    \tServerName example.com\n    \tServerAlias www.example.com\n    \tDocumentRoot \/var\/www\/example.com\n    \tServerAdmin webmaster@example.com\n    \tErrorLog \/var\/log\/apache2\/example_com_error.log\n    \tCustomLog \/var\/log\/apache2\/example_com_access.log combined\nRewriteEngine on\nRewriteCond %{SERVER_NAME} =www.example.com [OR]\nRewriteCond %{SERVER_NAME} =example.com\nRewriteRule ^ https:\/\/%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]\n&lt;\/VirtualHost&gt;<\/code><\/pre>\n\n\n\n<p>It is important to note that we utilize the `ServerAlias` directive instead of setting up a separate `&lt;VirtualHost&gt;` block for redirecting from www to non-www in Apache. This approach is more efficient and ensures that both www.example.com and example.com point to the same content. You must implement a rewrite rule to achieve the actual redirection.<\/p>\n\n\n\n<p>To enable mod_rewrite effectively, execute the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo a2enmod rewrite<\/code><\/pre>\n\n\n\n<p>To enable the configuration, we ran:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">a2ensite example_com<\/code><\/pre>\n\n\n\n<p>In Apache2, the management of virtual host configurations is handled through two key directories within the \/etc\/apache2\/ directory: sites-available and sites-enabled. Understanding how these directories work is crucial for effectively managing websites hosted on an Apache server.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>sites-available Directory:<\/strong> This directory contains configuration files for all available virtual hosts. Each file typically represents a separate website or domain that can be served by Apache. However, having a configuration file in sites-available does not mean that it is currently active or being served by Apache.<\/li>\n\n\n\n<li><strong>sites-enabled Directory:<\/strong> This directory contains symbolic links (symlinks) to the configuration files in sites-available that are currently active and being served by Apache. Only the configurations listed in sites-enabled will be read and used by Apache when it starts or reloads.<\/li>\n<\/ul>\n\n\n\n<p>To disable the default virtual host inside of the sites-available Directory, simply run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo a2dissite 000-default<\/code><\/pre>\n\n\n\n<p>That command removed a simlink to the the 000-default.conf file in \/etc\/apache2\/sites-available\/.&nbsp;<\/p>\n\n\n\n<p>And finally, we reloaded Apache to apply the changes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo systemctl reload apache2<\/code><\/pre>\n\n\n\n<p>Everything was set! Our virtual host was now live.<br><\/p>\n<\/details>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong>Step 7: <strong>Securing Apache with OpenSSL and Digital Certificates<\/strong><\/strong><br><\/summary>\n<p>Now that our site was running, we needed to make sure it was <strong>secure<\/strong>. By default, web servers communicate using <strong>HTTP<\/strong>, which sends data in plain text. That means passwords, personal information, and other sensitive data could be intercepted by hackers. To fix this, we needed <strong>HTTPS<\/strong>, which encrypts communication using <strong>SSL\/TLS certificates<\/strong>.<br><\/p>\n\n\n\n<p>The easiest way to get a certificate is by using <strong>Certbot<\/strong>, a tool that automatically configures <strong>Let\u2019s Encrypt SSL<\/strong> for Apache.<br><\/p>\n\n\n\n<p>Connect to our VPS using the command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo ssh -l root@your_droplet_ip<\/code><\/pre>\n\n\n\n<p>Install Certbot using the following commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo apt update\nsudo apt install certbot python3-certbot-apache<\/code><\/pre>\n\n\n\n<p>Request a digital certificate by running:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo certbot -d example.com<\/code><\/pre>\n\n\n\n<p>After requesting the certificate, make sure to specify the domain in the configuration file:\u00a0<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo \/etc\/apache2\/sites-available\/example_com.conf<\/code><\/pre>\n\n\n\n<p>Test the settings by reloading the website using the https prefix in your browser. Also, try typing the http prefix and check if it redirects you to https.<br><\/p>\n\n\n\n<p>For troubleshooting, check the log file:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo  \/var\/log\/letsencrypt\/letsencrypt.log<\/code><\/pre>\n\n\n\n<p>After completing all of those steps, the configuration file &#8216;example_com-le-ssl.conf` was created in the `\/etc\/apache2\/sites-available\/` directory.<br><\/p>\n<\/details>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong>Step 8: <strong><strong>Enabling module: HTTP Compression (mod_deflate)<\/strong><br><\/strong><\/strong><br><\/summary>\n<p>Our site was running smoothly, but we wanted to improve speed. One way to do this was by <strong>compressing<\/strong> files before sending them to users. Apache has a built-in module for this called <strong>mod_deflate<\/strong>.\u00a0<br><\/p>\n\n\n\n<p>Before we set up the compression, we looked at the compression level. The `DeflateCompressionLevel` setting controls how much compression is applied. Higher levels mean better compression but use more CPU power. The level can range from 1 (less compression) to 9 (more compression). For our project, we chose a compression level of 7, which gave us a 29% compression ratio.<br><br><\/p>\n\n\n\n<p>We enabled it with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo a2enmod deflate<\/code><\/pre>\n\n\n\n<p>Then, we configured it to compress text-based files:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo vim \/etc\/apache2\/mods-enabled\/deflate.conf<\/code><\/pre>\n\n\n\n<p>To set up the DeflateCompressionLevel to 7:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&lt;IfModule mod_deflate.c>\n    \t&lt;IfModule mod_filter.c>\n            \tAddOutputFilterByType DEFLATE text\/html text\/plain text\/xml text\/css text\/javascript\n            \tAddOutputFilterByType DEFLATE application\/x-javascript application\/javascript application\/ecmascript\n            \tAddOutputFilterByType DEFLATE application\/rss+xml\n            \tAddOutputFilterByType DEFLATE application\/wasm\n            \tAddOutputFilterByType DEFLATE application\/xml\n            \tDeflateCompressionLevel 7\n    \t&lt;\/IfModule>\n&lt;\/IfModule>\n<\/code><\/pre>\n\n\n\n<p>Create the CustomLog file. Go to the DocumentRoot:\u00a0<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">vim \/etc\/apache2\/sites-available\/example_com-le-ssl.conf<\/code><\/pre>\n\n\n\n<p>Add the following content to the file:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&lt;IfModule mod_ssl.c>\n&lt;VirtualHost *:443>\n    \tServerName example.com\n    \tServerAlias www.example.com\n    \tDocumentRoot \/var\/www\/example.com\n    \tServerAdmin webmaster@example.com\n    \tErrorLog \/var\/log\/apache2\/example_com_error.log\n    \t#CustomLog \/var\/log\/apache2\/example_com_access.log combined\n    \tCustomLog\t\"\/var\/log\/apache2\/deflate_log\" deflate\n    \tDeflateFilterNote\tratio\n    \tLogFormat\t'\"%r\" %b (%{ratio}n%%) \"%{User-agent}i\"' deflate\nInclude \/etc\/letsencrypt\/options-ssl-apache.conf\nSSLCertificateFile \/etc\/letsencrypt\/live\/www.example.com\/fullchain.pem\nSSLCertificateKeyFile \/etc\/letsencrypt\/live\/www.example.com\/privkey.pem\n&lt;\/VirtualHost>\n\n<\/code><\/pre>\n\n\n\n<p>Restart the web server:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo systemctl restart apache2<\/code><\/pre>\n\n\n\n<p>To measure the impact, we downloaded a large file from jQuery:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo cd \/var\/www\/example.com\/<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">wget https:\/\/code.jquery.com\/jquery-3.7.1.js<\/code><\/pre>\n\n\n\n<p>Then, we checked the logs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">tail \/var\/log\/apache2\/deflate_log<\/code><\/pre>\n\n\n\n<p>The result was a <strong>29% compression ratio<\/strong>, meaning our site loaded <strong>much faster<\/strong>!<\/p>\n<\/details>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong>Step 9: <strong><strong><strong>Tracking Server Status with Apache\u2019s Status Module<\/strong><br><\/strong><br><\/strong><\/strong><br><\/summary>\n<p>As our web server started handling more traffic, we realized we needed a way to monitor its performance. Was it running smoothly? Were there any slowdowns? Apache\u2019s <strong>status module<\/strong> provided real-time data about server activity, helping us troubleshoot any issues, such as incomplete log files or unusual traffic spikes.<br><\/p>\n\n\n\n<p>Enabling the module was simple. We ran:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo A2enmod status<\/code><\/pre>\n\n\n\n<p>Next, we configured it by editing the <strong>status.conf<\/strong> file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">vim \/etc\/apache2\/mods-available\/status.conf<\/code><\/pre>\n\n\n\n<p>Inside the file, we added:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&lt;IfModule mod_status.c>\n    \t# Allow server status reports generated by mod_status,\n    \t# with the URL of http:\/\/servername\/server-status\n    \t# Uncomment and change the \"192.0.2.0\/24\" to allow access from other hosts.\n    \t&lt;Location \/server-status>\n            \tSetHandler server-status\n            \tRequire local\n            \tRequire ip \\your public IP address\n    \t&lt;\/Location><\/code><\/pre>\n\n\n\n<p>To find our <strong>public IP address<\/strong>, we used:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">curl ident.me<\/code><\/pre>\n\n\n\n<p>After adding our IP address and restarting Apache, we could now access real-time server statistics at:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">http:\/\/example.com\/server-status<\/code><\/pre>\n\n\n\n<p>With this setup, we had a <strong>bird\u2019s-eye view<\/strong> of our server\u2019s performance, making it easier to detect and fix issues before they became major problems.<\/p>\n<\/details>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><strong>Step 10: <strong><strong><strong><strong>Installing PHP for Dynamic Content<\/strong><\/strong><br><\/strong><br><\/strong><\/strong><br><\/summary>\n<p>At that point, our web server could serve static HTML files, but we wanted more. We needed <strong>dynamic content<\/strong>, database interactions, and scripting capabilities. That\u2019s where <strong>PHP<\/strong> came in.<br><\/p>\n\n\n\n<p>PHP (Hypertext Preprocessor) is a powerful <strong>server-side scripting language<\/strong>. Unlike JavaScript, which runs on the user\u2019s browser, PHP executes on the server, generating dynamic web pages before sending them to visitors. It\u2019s widely used for building interactive websites, and it integrates seamlessly with <strong>MySQL databases<\/strong>.<\/p>\n\n\n\n<p>Connect to your VPC:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo ssh -l root@your_droplet_ip<\/code><\/pre>\n\n\n\n<p>To install PHP and its required modules, we ran:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo apt update &amp;&amp; apt install php php-mysql libapache2-mod-php\nsudo systemctl restart apache2\nsudo php --version<\/code><\/pre>\n\n\n\n<p>To test if PHP was working correctly, we created a small script:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">sudo vim \/var\/www\/example.com\/test.php<\/code><\/pre>\n\n\n\n<p>Inside the file, we added:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&lt;?php\n        php.info();\n?><\/code><\/pre>\n\n\n\n<p>Then, we opened our browser and visited <a href=\"https:\/\/taticloud.com\/test.php\">https:\/\/example.com\/test.php<\/a>.<\/p>\n\n\n\n<p>Seeing the <strong>PHP info page<\/strong> confirmed that PHP was up and running!<\/p>\n<\/details>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Build Your Portfolio Website with DevOps: A Scalable and Secure Approach Are you ready to showcase your skills as a DevOps engineer with a professional portfolio website? Imagine having a fully functional, cloud-hosted site that highlights your expertise and serves as a hands-on DevOps project to impress recruiters and clients. \ud83d\ude80 Through this project, I [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-688","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.taticloud.com\/index.php?rest_route=\/wp\/v2\/pages\/688","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.taticloud.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.taticloud.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.taticloud.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.taticloud.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=688"}],"version-history":[{"count":44,"href":"https:\/\/www.taticloud.com\/index.php?rest_route=\/wp\/v2\/pages\/688\/revisions"}],"predecessor-version":[{"id":793,"href":"https:\/\/www.taticloud.com\/index.php?rest_route=\/wp\/v2\/pages\/688\/revisions\/793"}],"wp:attachment":[{"href":"https:\/\/www.taticloud.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=688"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}