How I set up Amazon Lightsail as a testing environment.

How I set up Amazon Lightsail as a testing environment.

Getting Started

Firstly.. you need to have AWS account. If you don’t know how to create AWS account, you can follow the instructions provided here - AWS Docs. After ;you have created an account, log in to your account dashboard and search for “Lightsail” services. The Lightsail resources page will be opened in a new tab, and you can mess around with Lightsail resources from there.

You can just create a new instance from the web dashboard by clicking the “Create instance” button an the upper right corner and then you will be greeted by a page in which you can set configurations for the instacne you want to spin up.

Pre-launch configuration

In the “Create an instance” page, you will be given an option to pick your instance image. You can choose between Linux and Windows, and also Lightsail has some free blueprints that are preinstalled with the OS and you can choose between pre-installed apps + OS or the bare OS only. These blueprints include WordPress, Magento, Joomla, Gitlab CE, Nginx, Ghost, Drupal, Plesk, etc. Since I dont usually use any of these, I mostly go with the bare OS only, and then go with either Ubuntu or Debian.

Now, choose the plan or server type you want to use. $5, $7 and $12 tier are free for the first 90 days, so I usually go with those. You aslo need to specify which key you want to use to connect to the server via SSH. Most of the time, you will only need a default key to SSH into the server, but if you are planning to share the connection with someone else, then it is better to create a separate key for each servers so that we decrease the risk of making our servers vulnerable.

You can also write vanilla bash scripts in the “Launch Script” section so that you can update or install packages you want on the initiation sequence. You also have the option to add key-value pair tags so that you may be able to identify your resources easily.

IMPORTANT NOTE: It is VERY CRUCIAL that you download your private acces key for your servers and keep them in a safe location. If you lose the key, you cannot SSH into your server anymore. Whether you use the Lightsail provided default key or make a key of your own, once you download, YOU CANNOT DOWNLOAD IT AGAIN. So make sure you download it correctly and store them in a safe place.

Post-launch configuration

I have mentioned in the previous section that we can write a launch script when we spin up the instance. What I usually do is install Nginx, Certbot and add PHP PPA repository to apt list of repositories. I install PHP because we usually use Laravel framework to build our web apps. If I know beforehand the exact version of PHP i need to install, then I usually install it as well; but if I am not sure about the exact version I want to use, I install it later after cloning the project.

The launch script may look something like this:

sudo apt update && sudo apt upgrade -y
sudo add-apt-repository ppa:ondrej/php -y && sudo apt update
sudo apt install nginx certbot python3-certbot-nginx curl wget vim -y

In the above command, I update the apt repository and then upgrade all the packages that Ubuntu comes with. After that, I add the PHP PPA repository to apt and then I have to update again in order to tell the repository registry that I added the new PHP repo. Then, I proceed to install nginx, certbot and its plugin along with some other CLI tools like curl and wget.

This means that when the instance is ready and I SSH into it, nginx, certbot, etc will already be installed. Now, when trying to SSH into your Lightsail server, it is important first that the port is open in the server network configuration. You can check this by clicking at the server on the web console you just launched and then navigate to the ‘Network’ tab; there you can see a list of firewall rules that allows ports over IPv4 and IPv6.

By default, port 80 (HTTP) and port 22 (SSH) is open when you spin up the server. If you are planning to use https for ths server, you may as well allow port 443 in IPv4 firewall. IPv6 is not mandatory, but that is upto you.

Configure the server to serve web contents

Now, we will connect to the Lightsail server via SSH from our local terminal. Firstly, you have to know where you have downloaded the key file for the server. Then, you need to give the key file a read-only permission for you user so that no one can modify the file. You can do that using the following command:

sudo chmod 400 /path/your/key.pem

After setting the permission, you can SSH into the server by using the following command:

ssh -i /path/to/your/key.pem <username>@<public_ip_address OR domain_name>

By default, username for Ubuntu operating systems is ’ubuntu’ and you can see the public IPv4 address of the server instance from the Lightsail web dashboard. After SSH-ing into the server, you can check if the launch script you have initiated has been successfully executed or not by checking the service status of Nginx. You can do that using the following command:

sudo systemctl status nginx

if systemctl does not see any service running, it means that either your launch script has probably failed or apt is installing those services in the background. So you have to wait a little bit after launching your new instance to let the script take time to install the new services. Then you can SSH into the server and continue configuring as you need.

Cloning and setting up the project

If the script hasn’t successfully installed the packages you need, you can manually install them now. We can add the PHP PPA and install nginx using apt like the command we see at the beginning; but, we have yet to install php and other dependencies. And before that let’s clone the project first.

Generating SSH key in your server

We can use the ssh-keygen command to generate SSH key fingerprint for our server so that we can push or pull from the repository easily without having to input our credentials everytime we try to do so. By default, ssh-keygen uses RSA encryption to generate SSH keys, but I prefer to use ed25519 since the fingerprint fie can be a lot smaller and cleaner to maintain. Let’s generate the SSH key:

ssh-keygen -t ed25519

Here, the -t flag specifies the type of SSH key we want to make. Now you can just press Enter and leave the prompt fields as blank. If you run the above command as a normal user, your user or default ubuntu user, it will be stored in the home diretory of the user that runs this command. In my case I usually run this command as root user beacuse the location where I will install the project will be out of scope of the normal ubuntu user, so it is better to run it as root in order to not have permission conflicts.

Since I ran this command as root, the key file we generated will be stored at /root/.ssh/. In this directory, there will be files like id_ed25519 and id_ed25519.pub. We want the .pub file and not the private key. We will copy the contents of the public key file and then add it to our version control.

cat /root/.ssh/id_ed25519.pub

The above command will display the actual value of the SSH public key. Copy this value and then go to your version control ( either GItlab or Github). Then, go to the project you want to clone, and go to the project settings. Go to the repository settings and then add a deploy key. In the deploy key fields, input the name of the key and then paste the content you just copied from your terminal. Save it and then copy the “clone via SSH” link and then go back to your terminal.

For the sake of convenience, it is recommended you run the commands I provoded here as root user. Almost all of these directories are out of scope for the normal user ubuntuand if you encounter any problems, I recommend re-running the command as sudo. Now, navigate to /var/www and then clone the repository by using the command below:

git clone <your_repo_url> <your_directory_name>

Keep in mind that you do not need to create the directory beforehand, git will create it. You just have to specify which name you are going to give to the project directory.

Depending on which project you are cloning, you will need either PHP or NodeJS installed in your server. We will go through Laravel based PHP installation first. We will need to download composer and php binaries so that we can set up our project correctly.

Installing PHP and composer

You can verify the PHP and NodeJS version your project requires by taking a look at their package list files. For PHP, you can look at the composer.json file:

grep php composer.json

In order to run the above command, you must be in the root directory of your project (i.e. /var/www/<your_directory>). It will show you the minimum PHP version required for the project.

Then, if you need NodeJS installed, you can take a look at one of the files: yarn.lock,pakcage.json,pnpm.lock. Your developer may use different tools like yarn , npm or pnpm to manage packages, thus they produce different lock files for dependency management.

grep node package.json

The above command searches for the word ‘node’ in the package.json file. Like PHP, you will be able to see the minimum version needed for the project. Now, we will install the PHP and node binaries:

sudo apt install php8.3 php8.3-{curl,common,mysql,cli,zip,gd,mbstring,xml,intl} php8.3-fpm -y

I included php8.3-fpm because I am planning to use Nginx as a web server and we need the PHP FPM package to run the PHP processes in the background.

We can use the curl tool to download composer from their official site:

# Download the installer file in the /tmp directory:
curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php

# Installing composer using the PHP binary:
sudo php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer

Now, after successfully running this command, you can check composer to see if it is properly installed by checing its version:

composer --version

If it shows you the currently installed composer version, then you are good to go.

Manually installing a specific prebuilt NodeJS version

Installing NodeJS is a bit manual because Ubuntu’s apt repository only has node version 18.x.x as its latest version. Sometimes we need to use version 22.x.x and that is why I always manually download the prebuilt binary of exact version required by the project. Now, in order to download your required version of NodeJS, go to their official download site and look at the prebuilt binary section and select your CPU architecture type and OS type. Then, right click the download link and copy the link address.

Now go to your terminal and then cd into /opt. We will make use of the wget tool we installed earlier. For the sake of convenience, we will download node version 22.13.1 for example:

wget https://nodejs.org/dist/v22.13.1/node-v22.13.1-linux-x64.tar.xz

The file you downloaded will be a tarball ( .tar.xz file). You can extract the file by the following command:

tar xvf node-v22.13.1-linux-x64.tar.xz

Now, you will have a directory named node-v22.13.1-linux-x64 and a file named node-v22.13.1-linux-x64.tar.xz in /opt. You can remove the tar file since you allready extracted its contents. Then, you need to symlink the binaries to direactories that are alreay in the $PATH.

ln -s /opt/node-v22.13.1-linux-x64/bin/n* /usr/local/bin/

The above command will symlink three files: npm, node and npx from /opt/node-v22…/bin to /usr/local/bin/. Now, after symlinking these files, you can now check the node and npm version from the terminal using these commands:

node -v
npm -v

Installing database server (MariaDB/MySQL)

Installation of database server canbe done in one simple command:

sudo apt install mariadb-server -y

After a successful installation, you must run mariadb-secure-installation. This will help you set up the database server, prompt ou a root password ( it is empty, just press enter at first). Then, it will prompt you to change the root password; reply yes and the set the new password for root user. You need to remember this password since you are setting it for the root account. The secue installation script will help you remove test databases and test users and also reload table privileges. Say yes to all those prompts and then after you finish, you can enter the database CLI using the mysql or mariadb command. Now create a database using the following command:

CREATE DATABASE <your_db_name>;

You can also create a separate user for your project. This is important because if you want to connect this staging server’s database remotely from your local machine, you cannot connect using the root user. This is because, for safety measures, MariaDB doesn’t allow remote connection using root user of the database and thus it is better to create a separate user. You can do that by the following command:

CREATE USER admin@'%' identified by 'Somepassword@123';

The ’%’ sign repesents a wildcard for the hostname, which means that the user can connect from any host. Somepassword@123 is the password for the admin user; you can use whichever password you like, but you need to remember that .

Now, grant the user all privileges since this server will ony have one database:

GRANT ALL PRIVILEGES ON *.* TO admin@'%';
FLUSH PRIVILEGES;

The *.* in the above line means that we grant all available operations on all available databases. The first asterisk (*) represents all databases available in this server and the second asterisk (*) after the dot (.) represents all the available database operations like SELECT, CREATE, DROP, etc. This means that the admin user is given the highest privilege the same as the root user.

Installing & setting up the project

We can install laravel projects with one simple comopser install command. But is important to know that the PHP version you currently use is important. YOu ma have two or more different versions of PHP installed in your server. So make sure you change the PHP version to the right one before running composer install.

If you have NodeJS applications or have tailwind components in your laravel project, then you need torun npm install or yarn, etc.. depending upon the lockfiles you have for your project, and then your project dependencies willbe installed in no time.

Now copy the .env.example file to .env to configure envronment variables (you must be in your project root directory):

cp .env.example .env

Then you need to generate the key for the project:

php artisan key:generate

Create an empty log file for laravel logs:

touch storage/logs/laravel.log

Change the permission and ownership of public, bootstrap/cache and storage directories:

chown -R www-data: public/ storage/ bootstrap/cache/
chmod -R 764 storage/

Now we can link the storage folder to public using the following command:

php artisan storage:link

Now, we will edit the .env file. Open it with your editor of choice like pico, nano, vim, etc.

I will use vim as it is the one I like the most: vim .env

You can change the APP_NAME to the name of your project, and set the APP_URL to the domain name you are going to use for the server (mapping to its IP address is also fine).

Now, go down to the DB_CONNECTION section and then make sure the connection is set to mysql or mariadb and then DB_HOST will be localhost since we install the database in this server itself. DB_PORT is default (i.e. 3306), put the name of your database in the DB_DATABASE field and you must also input your database username and password in the DB_USERNAME and DB_PASSWORD respectively I recommend you use the admin user we have created instead of the root user.

if you are using AWS S3 Buckets, you must also fill in the access keys and bucket names in this .env file.

Go to the project root directory and now we are ready to run our migration and seed sample data and users:

php artisan migrate --seed

Configuring Nginx as a web server

The configuration files for Nginx is located at /etc/nginx. Go to that directory and edit the nginx.conf file first. Uncomment (remove the #) the line that says server_tokens off;. This conceals the nginx version we are using from the web view. Save and quit the file.

Remove all the sample config file from sites-enabled directory:

rm sites-enabled/*

Then, go to sites-available and create a file with the name of your project:

vim mywebsite

Now, inside the file paste the following contents:

server {

    listen 80 default_server;
    root /var/www/<project_dir_name>/public;

    # Add index.php to the list if you are using PHP
    index index.php index.html;

    server_name <your_domain_or_IP>;
    client_max_body_size 50M;

    location / {
        #try_files $uri $uri/ =404;
        try_files $uri $uri/ /index.php?$query_string;
    }

    # pass PHP scripts to FastCGI server
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        # With php-fpm (or other unix sockets):
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }

    access_log /var/log/nginx/access.log combined buffer=512k flush=1m;
    error_log /var/log/nginx/error.log warn;
}

Make sure to replace the directory name and the domain name or IP address as per your project. If your project uses different PHP version, you also need to install a corresponding php-fpm package. So, you will need to change this line:

fastcgi_pass unix:/run/php/php8.3-fpm.sock;

with the current version of PHP-FPM you have installed on the server. Save the file and then quit.

Symlink the file you have just created to /etc/nginx/sites-enabled directory for it to be detected by the running webserver:

ln -s /etc/nginx/sites-available/mywebsite /etc/nginx/sites-enabled/

Check the configuration of Nginx after you symlink:

nginx -t

If your configuration is ‘Ok’, then restart the nginx service:

sudo systemctl restart nginx

Generating SSL certificate using certbot

We have installed certbot and python3-certbot-nginx using apt earlier and we included the python certbot plugin for nginx because this plugin can read and modify our nginx configurations for us so that we do not need to manually edit the config files for SSL certificates. All we have to do is simply run this command as root:

certbot --nginx

This command will check our nginx config we have just symlinked to /etc/nginx/sites-enabled and then look at the server_name. It will ask if we want to generate SSL certificate for the domain name we have entered in our webserver config. In order for this certificate generation to be successful, we need to map the public IP address of this server to the domain DNS record before generating SSL certificate using certbot.

It will prompt you to enter your email ( this is for certificate renewal reminders), Prompt you to accept their ‘Terms & Conditions’; answer ‘y’ and it will prompt you again if you want to participate in digital freedom and such.. I always respond ‘n’ because they tend to follow you up with unwanted newsletters a bit too much. So, preferrably “No” for the second one.

Then, after successfully generating SSL certificate for your domain, you can visit the domain from the browser. Keep in mind that Amazon Lightsail does not open port 443 by default and you have to manually open the port in the web console. Click on the instance you want to open the port for and then navigate to the "Network” tab, and then under “IPv4 Firewall” Add a new rule and select HTTPS protocol. If you want to open port 443 for IPv6, keep the “Duplicate rule for IPv6” checkbox checked. You can uncheck it if you don’t want it to be open for IPv6, this is optional and totally upto you.

Conclusion

This is the basic workflow of how I usually set up a staging server for our projects in Amazon Lightsail. I know there are a lot of easier and more automated way to do things other than this. But I like to do it manually sometimes just so that I can know the working principles and architecture of these projects. I will try to improve this workflow in the future and might as well write a very versatile script to handle spinning up these servers.

Thank you for taking the time to read and I hope this is useful for you.