By the end you'll know
- What packages and repositories are, and why servers install software this way
- How to find, install, remove and update software with
apt - Where a package puts its program, its settings and its service
- How to set up a site in nginx, and check its settings before using them
- How to install PostgreSQL and make your first database
The problem: a server with nothing on it
The practice files describe a server with three parts: a web server in front, the books-api app in the middle, and a database behind it. Lesson 7 has the app running. This lesson installs the other two.
You could download each program from its website. But then you'd have to check it's genuine, find and install everything it depends on, put its files in the right places, set up its service, and repeat all of that for every security fix. On Linux, almost nobody does. Instead, the distribution packages the software for you.
On your own computer, in your practice folder, start the server and log in, as in Lesson 7:
cd linux-practice
vagrant up
vagrant ssh
Everything else in this lesson runs on the server.
Packages and repositories
A package is a program ready to install: its files, a list of the other packages it needs (its dependencies), and small scripts that set it up. On Ubuntu and Debian, packages are .deb files. A repository is a big, signed collection of packages that Ubuntu builds and tests together. Your server already knows where Ubuntu's are:
head -12 /etc/apt/sources.list.d/ubuntu.sources
Types: deb
URIs: http://ports.ubuntu.com/ubuntu-ports/
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
Types: deb
URIs: http://ports.ubuntu.com/ubuntu-ports/
Suites: noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
URIsis where to download from.ports.ubuntu.comis for processors other than Intel and AMD, like the arm64 in an Apple Silicon Mac; an Intel or AMD server usesarchive.ubuntu.com.Suitesare the release,noble(Ubuntu 24.04's name), plus its updates, newer extras (backports), and security fixes.Componentssort packages by who supports them:mainis supported by Ubuntu,universeby the community.Signed-Byis the key every package list must be signed with. A tampered list is refused, which is why this is safer than downloading programs by hand.
apt is the tool you'll use. Underneath it, dpkg does the actual installing of .deb files; apt adds downloading, dependencies and searching on top.
Update the list first
apt works from a local copy of each repository's list of packages. apt update downloads a fresh copy. It installs nothing:
sudo apt update
Hit:1 http://ports.ubuntu.com/ubuntu-ports noble InRelease
Get:2 http://ports.ubuntu.com/ubuntu-ports noble-updates InRelease [126 kB]
Get:3 http://ports.ubuntu.com/ubuntu-ports noble-backports InRelease [126 kB]
Get:4 http://ports.ubuntu.com/ubuntu-ports noble-security InRelease [126 kB]
...
Fetched 14.3 MB in 2s (8,805 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
238 packages can be upgraded. Run 'apt list --upgradable' to see them.
(Most of the Get: lines are left out.) Hit means that list hadn't changed; Get means it downloaded a new one. The last line says 238 installed packages have newer versions waiting: this server's image was built weeks ago. Updating them is apt upgrade, at the end of this lesson. Run apt update before installing anything, every time, so you get current versions.
Find it
apt search --names-only '^nginx$' 2>/dev/null
apt show nginx 2>/dev/null | grep -E '^(Package|Version|Depends|Description)'
apt-cache policy nginx
Sorting... Done
Full Text Search... Done
nginx/noble-updates,noble-security 1.24.0-2ubuntu7.19 arm64
small, powerful, scalable web/proxy server
Package: nginx
Version: 1.24.0-2ubuntu7.19
Depends: libc6 (>= 2.34), libcrypt1 (>= 1:4.1.0), libpcre2-8-0 (>= 10.22), libssl3t64 (>= 3.0.0), zlib1g (>= 1:1.1.4), iproute2, nginx-common (= 1.24.0-2ubuntu7.19)
Description: small, powerful, scalable web/proxy server
nginx:
Installed: (none)
Candidate: 1.24.0-2ubuntu7.19
Version table:
1.24.0-2ubuntu7.19 500
500 http://ports.ubuntu.com/ubuntu-ports noble-updates/main arm64 Packages
500 http://ports.ubuntu.com/ubuntu-ports noble-security/main arm64 Packages
1.24.0-2ubuntu7 500
500 http://ports.ubuntu.com/ubuntu-ports noble/main arm64 Packages
apt searchlooks through names and descriptions.--names-only '^nginx$'asks for exactlynginx; without it, you'd get dozens of nginx add-ons.2>/dev/nullhides a warning (Lesson 4) thataptprints when its output goes into a pipe.apt showdescribes a package.Dependslists what it needs, includingnginx-common, whichaptwill install for you.apt-cache policysays what's installed (nothing yet), which version would be installed (the candidate), and where each version comes from. The newest is innoble-security: it includes security fixes made since Ubuntu 24.04 came out. The-2ubuntu7.19part is Ubuntu's own revision of nginx 1.24.0.
Install nginx
nginx (said "engine x") is a web server: it answers web requests, serves files, and passes requests on to apps behind it, which makes it a reverse proxy.
sudo apt install -y nginx
The following additional packages will be installed:
nginx-common
...
The following NEW packages will be installed:
nginx nginx-common
0 upgraded, 2 newly installed, 0 to remove and 238 not upgraded.
...
Setting up nginx-common (1.24.0-2ubuntu7.19) ...
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service.
Setting up nginx (1.24.0-2ubuntu7.19) ...
...
No services need to be restarted.
(Some lines are left out.) -y answers "yes" to "Do you want to continue?". The Created symlink line is Lesson 7's enable: the package enabled nginx's service as part of installing it. On Ubuntu, a service package is usually started and enabled straight away. Its unit file is in /usr/lib/systemd/system/, the folder for packaged units, not the /etc/systemd/system/ you used for your own.
systemctl status nginx --no-pager | head -5
curl -sI http://localhost | head -3
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Fri 2026-10-30 00:13:39 UTC; 2s ago
Docs: man:nginx(8)
Process: 3253 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Fri, 30 Oct 2026 00:13:42 GMT
Running, enabled, and answering: curl -sI asks for just the headers of the reply, and 200 OK is nginx's welcome page. Notice ExecStartPre=: a command systemd runs before starting the service. Here it's nginx -t, which checks nginx's settings, so nginx with broken settings won't even try to start. You'll use nginx -t yourself in a moment.
Where it all went
which nginx
dpkg -S "$(which nginx)"
ls /etc/nginx /etc/nginx/sites-enabled
apt list --installed 2>/dev/null | wc -l
/usr/sbin/nginx
nginx: /usr/sbin/nginx
/etc/nginx:
conf.d fastcgi_params koi-win modules-available nginx.conf scgi_params sites-enabled uwsgi_params
fastcgi.conf koi-utf mime.types modules-enabled proxy_params sites-available snippets win-utf
/etc/nginx/sites-enabled:
default
617
Lesson 2's layout, used exactly as intended: the program in /usr/sbin (sbin is for programs that administrators run), its settings in /etc/nginx, its logs in /var/log/nginx. dpkg -S answers "which package does this file belong to?", useful when you find a file and wonder where it came from. dpkg -L nginx lists every file a package installed. And 617 packages are installed on this server in total.
The bookshop's site
nginx's settings for each website go in /etc/nginx/sites-available/, and a site is switched on by a symbolic link to it in sites-enabled/, the same idea as systemd's .wants folder in Lesson 7. Use the practice files' own site.conf:
cat /practice/config/web/site.conf
server {
listen 80;
server_name books.example.com;
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_read_timeout 30s;
}
location /static/ {
root /srv/books;
}
}
In plain words: listen on port 80, for the site books.example.com. Pass anything under /api/ to the app on this machine's port 8080, and give up after 30 seconds. Serve anything under /static/ straight from files in /srv/books. Every setting ends in ;. Switch it on, and the default welcome site off:
sudo cp /practice/config/web/site.conf /etc/nginx/sites-available/books
sudo ln -s /etc/nginx/sites-available/books /etc/nginx/sites-enabled/books
sudo rm /etc/nginx/sites-enabled/default
ls -l /etc/nginx/sites-enabled/
sudo mkdir -p /srv/books/static
echo "body { font-family: sans-serif; }" | sudo tee /srv/books/static/style.css
total 0
lrwxrwxrwx 1 root root 32 Oct 30 00:13 books -> /etc/nginx/sites-available/books
body { font-family: sans-serif; }
ln -s TARGET LINK makes a symbolic link. Removing default only removed the link; the welcome site's settings are still in sites-available, ready to switch back on. With it gone, the bookshop is the only site, so it answers every request, whatever name they use.
Check before you load. nginx -t tests the settings without touching the running server. Then reload applies them without dropping a single connection, unlike restart:
sudo nginx -t
sudo systemctl reload nginx
curl -s http://localhost/static/style.css
curl -si http://localhost/api/books | head -1
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
body { font-family: sans-serif; }
HTTP/1.1 502 Bad Gateway
The static file works. The API gives 502 Bad Gateway, exactly what the practice files' README.txt warned: "If the app listens somewhere else, requests fail with 502 Bad Gateway." (A request sent the instant after a reload can still reach the old settings, and get the old site's answer. Give it a second.) nginx's logs say why:
sudo tail -1 /var/log/nginx/error.log
sudo tail -2 /var/log/nginx/access.log
2026/10/30 00:16:50 [error] 3399#3399: *5 connect() failed (111: Connection refused) while connecting to upstream, client: 127.0.0.1, server: books.example.com, request: "GET /api/books HTTP/1.1", upstream: "http://127.0.0.1:8080/api/books", host: "localhost"
127.0.0.1 - - [30/Oct/2026:00:16:50 +0000] "GET /static/style.css HTTP/1.1" 200 34 "-" "curl/8.5.0"
127.0.0.1 - - [30/Oct/2026:00:16:50 +0000] "GET /api/books HTTP/1.1" 502 166 "-" "curl/8.5.0"
Connection refused while connecting to the upstream, the app behind nginx, at 127.0.0.1:8080. Nothing is listening there: your books-api writes log lines, but doesn't answer web requests at all. The access log is the same format as the practice files' access.log from Lessons 3 and 4, now written by your own server. The 502s in those practice logs, between 14:00 and 14:20, looked exactly like this. Lesson 9 is about ports, and makes the app answer.
Install PostgreSQL
PostgreSQL is the database the app's settings point at: database_port = 5432 in app.conf.
sudo apt install -y postgresql
The following additional packages will be installed:
libcommon-sense-perl libjson-perl libjson-xs-perl libllvm17t64 libpq5 libtypes-serialiser-perl postgresql-16 postgresql-client-16
postgresql-client-common postgresql-common ssl-cert
...
0 upgraded, 12 newly installed, 0 to remove and 238 not upgraded.
Need to get 42.3 MB of archives.
After this operation, 176 MB of additional disk space will be used.
...
Creating new PostgreSQL cluster 16/main ...
/usr/lib/postgresql/16/bin/initdb -D /var/lib/postgresql/16/main --auth-local peer --auth-host scram-sha-256 --no-instructions
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
...
Setting up postgresql (16+257build1.1) ...
One command, 12 packages: postgresql itself is a small package that depends on the real one, postgresql-16, which depends on the rest. That's dependencies doing their job. The package also made its first database storage (a cluster) in /var/lib/postgresql, Lesson 2's place for data that changes, and a system user to own it, like your bookshop in Lesson 5:
systemctl list-units 'postgresql*' --no-pager
id postgres
UNIT LOAD ACTIVE SUB DESCRIPTION
postgresql.service loaded active exited PostgreSQL RDBMS
postgresql@16-main.service loaded active running PostgreSQL Cluster 16-main
Legend: LOAD → Reflects whether the unit definition was properly loaded.
ACTIVE → The high-level unit activation state, i.e. generalization of SUB.
SUB → The low-level unit activation state, values depend on unit type.
2 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.
uid=108(postgres) gid=110(postgres) groups=110(postgres),109(ssl-cert)
Two units: postgresql.service just starts the others and exits, and postgresql@16-main.service is the real database, running. The @ means one unit file used for many: one per cluster, here version 16's main. RDBMS stands for relational database management system.
Talk to it. psql is PostgreSQL's command-line client, and sudo -u postgres runs it as the postgres user:
sudo -u postgres psql -c 'SELECT version();'
sudo -u postgres createdb books
sudo -u postgres psql -l | head -8
version
------------------------------------------------------------------------------------------------------------------------------------------------
PostgreSQL 16.15 (Ubuntu 16.15-0ubuntu0.24.04.1) on aarch64-unknown-linux-gnu, compiled by gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0, 64-bit
(1 row)
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
-----------+----------+----------+-----------------+-------------+-------------+------------+-----------+-----------------------
books | postgres | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
postgres | postgres | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | |
template0 | postgres | UTF8 | libc | en_US.UTF-8 | en_US.UTF-8 | | | =c/postgres +
The bookshop has an empty books database. Why sudo -u postgres and no password? The install line said --auth-local peer: on this machine, PostgreSQL trusts that the Linux user postgres is the database user postgres, so Lesson 5's users and permissions decide who gets in. The SQL track picks up from here, with tables and queries.
What's listening? A preview of Lesson 9:
sudo ss -tlnp | grep -E 'nginx|postgres'
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=3399,fd=5),("nginx",pid=3398,fd=5),("nginx",pid=3284,fd=5))
LISTEN 0 200 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=5562,fd=6))
nginx listens on port 80, on every address (0.0.0.0). PostgreSQL listens on 5432, but only on 127.0.0.1, this machine, which is the safe default for a database. The app's settings say database_host = db.internal, a different machine's name. Both things are Lesson 9's to untangle.
Three tiers
What you've built has a name. Splitting an application into a front, a middle and a back is called a three-tier architecture, and it's one of the most common designs on the web:
| Tier | Its job | On this server | In the cloud, often |
|---|---|---|---|
| Web, or presentation | Takes requests from visitors, serves files, passes the rest on | nginx, port 80 | A load balancer, then web servers |
| Application | Does the work: the bookshop's own code | books-api, port 8080 (soon) | Several app servers, or containers |
| Data | Keeps everything that has to last | PostgreSQL, port 5432 | A managed database, like Amazon RDS |
Each tier only talks to the one next to it: visitors reach nginx, nginx reaches the app, and only the app reaches the database. That's why the split is worth having. Each tier can be grown on its own (more app servers on a busy day, a bigger database as data grows), and each can be locked down on its own: the database never needs to be reachable from the internet at all, which is exactly why PostgreSQL listens only on 127.0.0.1.
Here, all three tiers share one machine, which is normal for a small site and for learning. On a bigger setup, each tier is its own set of machines, and the connections between them cross a network. That's where the app's database_host = db.internal comes from: a database on a different machine, found by name.
Larger organisations often go a step further and split the application tier itself. Instead of one books-api doing everything, there might be a catalog service, an orders service and a payments service, each a small app with its own team, its own releases, and sometimes its own database. These are microservices, and they talk to each other over the network, usually as containers managed by Kubernetes. One app doing everything is called a monolith. Neither is better by default: microservices let big teams work and scale separately, at the cost of many more moving parts to deploy, connect and debug. Plenty of busy sites run happily on a monolith. Either way, the three tiers are still there; the middle one just has more boxes in it.
Lesson 9 is about the connections between them, and the AWS track builds the same three tiers as separate cloud services.
Remove and purge
Install a package, then take it away again. Try the commands in any order here first:
Removing comes in three strengths, and the difference matters on a real server. You'll see them for real in "Break it on purpose", below:
| Command | Removes the program | Removes its settings in /etc | Removes what it pulled in |
|---|---|---|---|
apt remove NAME | Yes | No: kept, in case you reinstall | No |
apt purge NAME | Yes | Yes | No |
apt autoremove | No | No | Yes: packages nothing needs any more |
None of them touch your own data, like a database or a website's files. That's on purpose.
Keeping it up to date
apt list --upgradable 2>/dev/null | wc -l
systemctl is-enabled unattended-upgrades
239
not-found
238 updates waiting (the 239th line is the Listing... header). sudo apt upgrade installs them all; on a real server, you'd read the list first, then run it in a quiet moment, since services restart as their packages update. Run it now if you like: it takes a few minutes, and nothing in the lessons depends on it.
Security fixes shouldn't wait for you to remember, though. Ubuntu's answer is unattended-upgrades, a service that installs security updates by itself, every day. Ubuntu's cloud servers come with it, but not-found says this Vagrant box doesn't. That's worth knowing: never assume a server has something because "Ubuntu always does". Check, and install what's missing:
sudo apt install -y unattended-upgrades
systemctl is-enabled unattended-upgrades
systemctl is-active unattended-upgrades
cat /etc/apt/apt.conf.d/20auto-upgrades
The following NEW packages will be installed:
python3-distro-info unattended-upgrades
...
Creating config file /etc/apt/apt.conf.d/20auto-upgrades with new version
Creating config file /etc/apt/apt.conf.d/50unattended-upgrades with new version
Created symlink /etc/systemd/system/multi-user.target.wants/unattended-upgrades.service → /usr/lib/systemd/system/unattended-upgrades.service.
...
enabled
active
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
(Some lines are left out.) Enabled and running, like every service package so far. 20auto-upgrades switches it on: refresh the package list every 1 day, and install updates every 1 day. Which updates is set in 50unattended-upgrades; by default, only security fixes, the ones that are safest to install without a person watching. Bigger updates stay your decision, with apt upgrade.
Break it on purpose
A name that doesn't exist.
sudo apt install nginxx
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
E: Unable to locate package nginxx
E: means error. A typo, or a package with a different name than you'd guess. apt search finds the real one. The same error on a brand-new server, for a name that's definitely right, usually means apt update hasn't been run yet.
A broken setting, and the safety net. Take the ; off listen 80:
sudo sed -i 's/listen 80;/listen 80/' /etc/nginx/sites-available/books
sudo nginx -t
sudo systemctl reload nginx
systemctl is-active nginx
2026/10/30 00:14:10 [emerg] 5678#5678: invalid parameter "server_name" in /etc/nginx/sites-enabled/books:3
nginx: configuration file /etc/nginx/nginx.conf test failed
Job for nginx.service failed.
See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.
active
Without the ;, nginx read listen 80 server_name books.example.com as one setting, so the error points at line 3, the line after the real mistake. Missing ; errors often do that: look at the line before. The reload failed, but nginx is still active, still serving the old, working settings. A failed reload never takes a site down; a failed restart would, because it stops first. That's the case for nginx -t, then reload, every time. Put it back:
sudo sed -i 's/listen 80$/listen 80;/' /etc/nginx/sites-available/books
sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Two web servers, one port. Apache (apache2) is the other big web server; it did most of the web's work before nginx, and still runs a lot of it. Install it next to nginx:
sudo apt install -y apache2
systemctl status apache2 --no-pager | head -6
sudo journalctl -u apache2 --no-pager -n 6
× apache2.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/apache2.service; enabled; preset: enabled)
Active: failed (Result: exit-code) since Fri 2026-10-30 00:14:13 UTC; 1s ago
Docs: https://httpd.apache.org/docs/2.4/
CPU: 8ms
Oct 30 00:14:13 bookshop apachectl[6197]: (98)Address already in use: AH00072: make_sock: could not bind to address 0.0.0.0:80
Oct 30 00:14:13 bookshop apachectl[6197]: no listening sockets available, shutting down
Oct 30 00:14:13 bookshop apachectl[6197]: AH00015: Unable to open logs
Oct 30 00:14:13 bookshop systemd[1]: apache2.service: Control process exited, code=exited, status=1/FAILURE
Oct 30 00:14:13 bookshop systemd[1]: apache2.service: Failed with result 'exit-code'.
Oct 30 00:14:13 bookshop systemd[1]: Failed to start apache2.service - The Apache HTTP Server.
The package installed fine; the service didn't start. Address already in use ... 0.0.0.0:80: only one program can listen on a port, and nginx got there first. Lesson 9 covers finding who holds a port. Now take Apache away, in stages:
sudo apt remove -y apache2
dpkg -l apache2 | tail -1
sudo apt purge -y apache2
sudo apt autoremove -y
The following packages were automatically installed and are no longer required:
apache2-bin apache2-data apache2-utils libapr1t64 libaprutil1-dbd-sqlite3 libaprutil1-ldap libaprutil1t64 liblua5.4-0
Use 'sudo apt autoremove' to remove them.
The following packages will be REMOVED:
apache2
...
rc apache2 2.4.58-1ubuntu8.17 arm64 Apache HTTP Server
...
Purging configuration files for apache2 (2.4.58-1ubuntu8.17) ...
dpkg: warning: while removing apache2, directory '/var/www/html' not empty so not removed
...
The following packages will be REMOVED:
apache2-bin apache2-data apache2-utils libapr1t64 libaprutil1-dbd-sqlite3 libaprutil1-ldap libaprutil1t64 liblua5.4-0
0 upgraded, 0 newly installed, 8 to remove and 238 not upgraded.
(Some lines are left out.) After remove, dpkg -l shows rc: removed, but its settings are still there. remove even says which 8 packages are now unneeded. purge deleted the settings, and left /var/www/html alone because it wasn't empty: nginx's welcome page lives there too, and apt never deletes files it didn't create. autoremove cleared the 8 extra packages.
On a real server
apt update, then read whatapt upgradewill change, then upgrade. Many teams do this weekly, and letunattended-upgradeshandle security fixes in between.Could not get lock /var/lib/dpkg/lock-frontendmeans anotheraptis already running, oftenunattended-upgradeson a new server's first boot. Wait a few minutes and try again. Never delete the lock file.- Does it need a reboot? A kernel update does. If
/var/run/reboot-requiredexists, the server is waiting for one. TheScanning processes...lines after an install are a tool called needrestart, checking which services need restarting. - Hold a version with
sudo apt-mark hold NAME, so upgrades leave it alone, for example a database you'll upgrade by hand.apt-mark unhold NAMEreleases it. - Software that isn't in Ubuntu's repositories comes from its maker's own repository (Docker and PostgreSQL both offer one), a
.debfile, or a snap, Ubuntu's other package format. Be wary of instructions that saycurl ... | sudo bash: that runs someone's script as root, unchecked.
Spot the problem
Work out what's wrong before you read the explanation.
It worked on the old server. A setup script runs sudo apt install -y nginx on a brand-new cloud server, and fails with E: Unable to locate package nginx. The same script works on an older server. nginx definitely exists. What's missing? The new server's package list is empty, or out of date, because apt update has never run on it. Scripts should always run sudo apt update before apt install.
Keep this
| To | Run |
|---|---|
| Refresh the package list (do this first) | sudo apt update |
| Find a package | apt search NAME, exactly: apt search --names-only '^NAME$' |
| Describe it | apt show NAME |
| Installed and available versions | apt-cache policy NAME |
| Install | sudo apt install -y NAME |
| Remove, keeping settings | sudo apt remove NAME |
| Remove with its settings | sudo apt purge NAME |
| Clear packages nothing needs | sudo apt autoremove |
| What can be updated | apt list --upgradable |
| Update everything | sudo apt upgrade |
| Which package owns a file | dpkg -S /usr/sbin/nginx |
| Every file in a package | dpkg -L NAME |
Installed, or only settings left (rc) | dpkg -l NAME |
| Check nginx's settings | sudo nginx -t |
| Apply them without dropping connections | sudo systemctl reload nginx |
| Switch an nginx site on | sudo ln -s /etc/nginx/sites-available/NAME /etc/nginx/sites-enabled/NAME |
| nginx's logs | /var/log/nginx/access.log, error.log |
| Run a PostgreSQL command | sudo -u postgres psql -c 'SELECT version();' |
| Make a database, list them | sudo -u postgres createdb NAME, sudo -u postgres psql -l |
Where you are now
The server has all three parts: nginx in front, serving files and passing /api/ on; the app in the middle; PostgreSQL behind, with a books database. But they're not connected. nginx gets Connection refused from port 8080, and the database only listens to itself. Lesson 9 is networking from the shell: ports, who's listening on them, and getting the three to talk, including the trap from Lesson 3, the port that's set twice.
Check yourself
What does sudo apt update do?
One apt install postgresql command installed 12 packages. Why?
Why run sudo nginx -t before reloading nginx?
nginx answers 502 Bad Gateway for /api/. What does that mean?
Apache failed to start with Address already in use for port 80. Why?
After sudo apt remove apache2, dpkg -l shows rc. What does that mean?
Key terms
- Package: a program ready to install, with its dependencies and setup scripts. A
.debfile on Ubuntu. - Repository: a signed collection of packages to install from.
- Dependency: another package a package needs to work.
- apt and dpkg: the everyday package tool, and the lower-level tool it uses to install
.debfiles. - Web server and reverse proxy: a program that answers web requests, and one that passes them on to an app behind it. nginx is both.
- Upstream: the app a reverse proxy passes requests to.
- Database cluster: in PostgreSQL, one set of databases managed by one running server.
Go deeper
dpkg -L nginx-common | grep etclists every settings file the nginx package gave you.apt-cache rdepends --installed libpq5shows which installed packages depend onlibpq5, the other way round fromapt show.- Read
/etc/nginx/nginx.conf: it's short, and theincludelines show howsites-enabledgets loaded. - Lesson 7, Services and logs with systemd, for
systemctlandjournalctl, which every service here uses.