Home Database PostgreSQL Installation and Setup: A Complete Beginner's Guide

PostgreSQL Installation and Setup: A Complete Beginner's Guide

In Plain English 🔥
Think of PostgreSQL as a super-organised filing cabinet for your app's data. Just like a filing cabinet needs to be physically placed in your office before you can use it, PostgreSQL needs to be installed on your computer before your apps can store anything. Once it's installed, you get a 'filing cabinet manager' (called psql) that lets you create drawers (databases), folders (tables), and actually put papers (data) inside. This guide is about getting that cabinet delivered, assembled, and ready to use.
⚡ Quick Answer
Think of PostgreSQL as a super-organised filing cabinet for your app's data. Just like a filing cabinet needs to be physically placed in your office before you can use it, PostgreSQL needs to be installed on your computer before your apps can store anything. Once it's installed, you get a 'filing cabinet manager' (called psql) that lets you create drawers (databases), folders (tables), and actually put papers (data) inside. This guide is about getting that cabinet delivered, assembled, and ready to use.

Every app you use — Instagram, your bank's website, your company's HR system — stores data somewhere. That 'somewhere' is almost always a relational database, and PostgreSQL is one of the most trusted ones on the planet. It's free, open-source, battle-hardened by decades of production use, and it powers everything from small side projects to systems at Apple, Reddit, and Spotify. If you're learning backend development or data engineering, understanding how to get PostgreSQL running locally is literally step one.

What PostgreSQL Actually Is (Before You Install Anything)

PostgreSQL (often called 'Postgres') is a relational database management system — or RDBMS. That's a fancy way of saying it's software that stores data in organised tables, like spreadsheets that can talk to each other. It runs as a background service (a server) on your machine, quietly waiting for instructions. You then connect to it using a client — either the built-in command-line tool called psql, a graphical tool like pgAdmin, or your application's code.

Here's the mental model that makes everything click: PostgreSQL is the engine, your database is the garage it runs in, and your tables are the shelves inside that garage. When you install PostgreSQL, you're installing the engine. Everything else — databases, tables, data — you create yourself after installation.

PostgreSQL supports SQL (Structured Query Language), which is the universal language for talking to relational databases. Every command you type — whether to create a table or fetch some records — is written in SQL. The good news is that SQL reads almost like plain English, so it's one of the friendliest languages to learn.

check_postgres_version.sh · BASH
1234567891011121314
# After installation, run this command in your terminal to confirm PostgreSQL installed correctly.
# The '--version' flag asks PostgreSQL to report which version is running.
# If you see a version number, the installation worked. If you see 'command not found', it didn't.

psql --version

# You should also check that the PostgreSQL server process is running.
# On Linux and macOS this uses 'pg_isready', a built-in diagnostic tool.
# It pings the server and tells you whether it's accepting connections.

pg_isready

# On Windows, you can check via Services (services.msc) or run:
sc query postgresql-x64-16
▶ Output
psql (PostgreSQL) 16.2
/var/run/postgresql:5432 - accepting connections
🔥
Quick Clarification:PostgreSQL and MySQL are both relational databases, but they're separate products made by different organisations. PostgreSQL is known for stricter standards compliance, better support for complex queries, and powerful features like JSON storage and full-text search. MySQL is historically more common in web hosting. For new projects, PostgreSQL is generally the stronger choice.

Installing PostgreSQL on Windows, macOS, and Linux

Installation differs by operating system, but the end result is the same: a running PostgreSQL server on port 5432 (the default), a superuser account called 'postgres', and the psql command-line tool available in your terminal.

Windows: The easiest path is the EnterpriseDB graphical installer at postgresql.org/download/windows. It walks you through everything with a GUI, installs pgAdmin (a visual database browser), and sets up PostgreSQL as a Windows Service that starts automatically on boot.

macOS: The cleanest method for developers is Homebrew — the macOS package manager. If you don't have Homebrew, install it first from brew.sh. One command then handles the whole PostgreSQL setup. Homebrew also makes upgrades and uninstalls trivial later.

Linux (Ubuntu/Debian): Use apt, the built-in package manager. The PostgreSQL project maintains its own apt repository so you always get the latest stable version rather than an older distribution-packaged one.

Regardless of your OS, after installation you'll need to do two things: start the server, and switch to (or authenticate as) the default 'postgres' superuser account to run your first commands.

install_postgres_all_platforms.sh · BASH
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
# ─────────────────────────────────────────────
# WINDOWS (run in PowerShell as Administrator)
# ─────────────────────────────────────────────
# Download the installer from https://www.postgresql.org/download/windows/
# Then run it and follow the GUI steps. After installation, verify with:
psql --version


# ─────────────────────────────────────────────
# macOS (using Homebrew)
# ─────────────────────────────────────────────

# Step 1: Install Homebrew if you don't have it yet
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Step 2: Install PostgreSQL (version 16 is current stable)
# 'brew install' downloads and installs the package automatically
brew install postgresql@16

# Step 3: Add PostgreSQL to your PATH so terminal can find the psql command
# Replace '~/.zshrc' with '~/.bash_profile' if you use bash instead of zsh
echo 'export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

# Step 4: Start the PostgreSQL server.
# 'brew services start' registers it so it also auto-starts on every reboot
brew services start postgresql@16


# ─────────────────────────────────────────────
# LinuxUbuntu / Debian
# ─────────────────────────────────────────────

# Step 1: Add the official PostgreSQL apt repository
# This ensures we get v16, not an outdated Ubuntu-packaged version
sudo sh -c 'echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'

# Step 2: Import the repository signing key so apt trusts it
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -

# Step 3: Refresh the package list and install PostgreSQL
sudo apt-get update
sudo apt-get install -y postgresql-16

# Step 4: Start the service and enable it to start on system boot
sudo systemctl start postgresql
sudo systemctl enable postgresql

# Step 5: Confirm the service is running
sudo systemctl status postgresql
▶ Output
● postgresql.service - PostgreSQL RDBMS
Loaded: loaded (/lib/systemd/system/postgresql.service; enabled)
Active: active (running) since Mon 2024-06-10 09:14:32 UTC; 3s ago

psql (PostgreSQL) 16.2
⚠️
Watch Out — Windows PATH Issue:On Windows, the installer adds psql to your PATH automatically, but only for new terminal windows opened after installation. If you have a terminal open during install and type 'psql', you'll get 'command not found'. Close the terminal completely and open a fresh one — that almost always fixes it.

First Login, Creating a Database, and Running Your First SQL

Once PostgreSQL is installed and running, you need to connect to it. By default, PostgreSQL creates a system user and a database superuser both named 'postgres'. Think of 'postgres' as the master key that unlocks everything — use it to set things up, but don't use it for your actual app later (more on that in the Gotchas section).

On Linux, you connect by switching to the 'postgres' system user first, then launching psql. On macOS (Homebrew install), your own system user is set up as a superuser automatically, so you can just run psql directly. On Windows, psql is available in the Start Menu under PostgreSQL.

Once inside psql, you'll see the prompt 'postgres=#'. This is your SQL command center. Everything you type here gets executed against the database. SQL commands end with a semicolon — forget the semicolon and psql just waits for more input, which trips up almost every beginner at least once.

The workflow for a brand-new project is always the same: create a database, connect to it, create a table, and insert some rows. Let's do exactly that.

first_database_setup.sql · SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
-- ─────────────────────────────────────────────
-- STEP 1: Connect to PostgreSQL
-- ─────────────────────────────────────────────

-- On Linux, first switch to the postgres system user in your terminal:
-- $ sudo -i -u postgres
-- $ psql

-- On macOS (Homebrew) or Windows, just open a terminal and run:
-- $ psql -U postgres

-- You should now see the psql prompt: postgres=#


-- ─────────────────────────────────────────────
-- STEP 2: Create a new database for a bookshop app
-- ─────────────────────────────────────────────

-- CREATE DATABASE makes a fresh, empty database.
-- Think of this as buying a brand-new filing cabinet.
CREATE DATABASE bookshop_db;

-- Verify it was created. '\l' is a psql shortcut that lists all databases.
\l


-- ─────────────────────────────────────────────
-- STEP 3: Connect to the new database
-- ─────────────────────────────────────────────

-- '\c' stands for 'connect'. Without this, your commands run against
-- the default 'postgres' database, not your new bookshop_db.
\c bookshop_db

-- The prompt changes to: bookshop_db=#
-- That '#' confirms you're now inside bookshop_db.


-- ─────────────────────────────────────────────
-- STEP 4: Create a table to store book records
-- ─────────────────────────────────────────────

-- A table is like a spreadsheet with defined column types.
-- SERIAL = auto-incrementing integer (PostgreSQL assigns the ID automatically)
-- VARCHAR(255) = text up to 255 characters
-- NUMERIC(10,2) = a number with up to 2 decimal places (perfect for prices)
-- NOT NULL = this column can never be left empty

CREATE TABLE books (
    book_id     SERIAL PRIMARY KEY,        -- Unique ID auto-assigned to every book
    title       VARCHAR(255) NOT NULL,     -- The book's title, required
    author_name VARCHAR(150) NOT NULL,     -- Author's full name, required
    price       NUMERIC(10, 2) NOT NULL,   -- Price in dollars, e.g. 12.99
    published_year INT                     -- Year published, optional
);


-- ─────────────────────────────────────────────
-- STEP 5: Insert some real book data
-- ─────────────────────────────────────────────

-- INSERT INTO adds rows to the table.
-- We don't supply book_id because SERIAL handles that automatically.
INSERT INTO books (title, author_name, price, published_year)
VALUES
    ('The Pragmatic Programmer', 'David Thomas', 39.99, 2019),
    ('Clean Code',               'Robert Martin', 34.50, 2008),
    ('Designing Data-Intensive Applications', 'Martin Kleppmann', 49.99, 2017);


-- ─────────────────────────────────────────────
-- STEP 6: Read the data back
-- ─────────────────────────────────────────────

-- SELECT * means 'give me every column'.
-- FROM books tells PostgreSQL which table to read from.
SELECT * FROM books;


-- ─────────────────────────────────────────────
-- STEP 7: Create a dedicated app user (best practice)
-- ─────────────────────────────────────────────

-- Never connect your app directly with the 'postgres' superuser.
-- Create a limited user that only has access to bookshop_db.
CREATE USER bookshop_app WITH PASSWORD 'use_a_strong_password_here';

-- GRANT gives this user permission to read/write in our database
GRANT ALL PRIVILEGES ON DATABASE bookshop_db TO bookshop_app;

-- Also grant access to the tables inside the database
\c bookshop_db
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO bookshop_app;

-- Confirm the user was created
\du
▶ Output
CREATE DATABASE

List of databases
Name | Owner | Encoding | Collate | Ctype
---------------+----------+----------+------------+------------
bookshop_db | postgres | UTF8 | en_US.UTF8 | en_US.UTF8
postgres | postgres | UTF8 | en_US.UTF8 | en_US.UTF8

You are now connected to database "bookshop_db" as user "postgres".

CREATE TABLE

INSERT 0 3

book_id | title | author_name | price | published_year
---------+----------------------------------------+-------------------+-------+----------------
1 | The Pragmatic Programmer | David Thomas | 39.99 | 2019
2 | Clean Code | Robert Martin | 34.50 | 2008
3 | Designing Data-Intensive Applications | Martin Kleppmann | 49.99 | 2017
(3 rows)

CREATE ROLE
GRANT

List of roles
Role name | Attributes
---------------+------------------------------------------------------------
bookshop_app |
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS
⚠️
Pro Tip — The Semicolon Rule:In psql, every SQL statement must end with a semicolon (;). If you press Enter and see ' ->' instead of getting a result, psql is waiting for the semicolon. Just type ';' and press Enter — it will execute what you typed. This is the single most common confusion for beginners in their first psql session.

Connecting to PostgreSQL from a Node.js or Python App

Running SQL in psql is great for learning and administration, but in real projects you'll connect from code — a Node.js backend, a Python data script, a Django web app. PostgreSQL doesn't care what language you use, as long as your code sends standard SQL over the network.

Every language has a 'driver' or 'adapter' — a small library that handles the low-level work of opening a connection, sending your SQL, and getting results back. In Python, the most popular driver is psycopg2. In Node.js, it's the 'pg' package.

The connection always needs five pieces of information: the host (usually 'localhost' for local development), the port (5432 by default), the database name, the username, and the password. These are called connection parameters and you should never hardcode them in your source code — use environment variables instead, which keeps passwords out of version control.

Below is a working example in both Python and Node.js that connects to the bookshop_db we created, queries the books table, and prints the results.

connect_to_bookshop_db.py · PYTHON
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
# ─────────────────────────────────────────────
# PYTHON EXAMPLE — using psycopg2
# ─────────────────────────────────────────────

# First, install the driver:
# pip install psycopg2-binary

import psycopg2  # The PostgreSQL driver for Python
import os        # Used to read environment variables safely

# Read connection details from environment variables.
# Set these in your terminal before running:
#   export DB_HOST=localhost
#   export DB_NAME=bookshop_db
#   export DB_USER=bookshop_app
#   export DB_PASSWORD=use_a_strong_password_here

db_host     = os.getenv("DB_HOST", "localhost")       # Default to localhost
db_name     = os.getenv("DB_NAME", "bookshop_db")     # Our database name
db_user     = os.getenv("DB_USER", "bookshop_app")    # Limited app user
db_password = os.getenv("DB_PASSWORD", "")             # Never hardcode this

try:
    # psycopg2.connect opens an actual TCP connection to PostgreSQL.
    # Until this line runs successfully, no SQL can be sent.
    connection = psycopg2.connect(
        host=db_host,
        dbname=db_name,
        user=db_user,
        password=db_password,
        port=5432  # PostgreSQL's default port
    )

    # A cursor is like a temporary workspace for running one SQL statement.
    # You can have multiple cursors open on the same connection.
    cursor = connection.cursor()

    # Execute a SQL query — this does NOT return results yet, just runs the query
    cursor.execute("SELECT book_id, title, author_name, price FROM books ORDER BY price DESC;")

    # fetchall() retrieves every result row as a list of tuples
    all_books = cursor.fetchall()

    print(f"Found {len(all_books)} books in the database:\n")

    # Loop through the results and print each book
    for book_id, title, author_name, price in all_books:
        print(f"  [{book_id}] {title} by {author_name} — ${price}")

except psycopg2.OperationalError as connection_error:
    # This error fires when PostgreSQL isn't running, credentials are wrong,
    # or the database name doesn't exist. The error message tells you which.
    print(f"Could not connect to the database: {connection_error}")

finally:
    # Always close the cursor and connection when you're done.
    # Leaving connections open wastes server resources.
    if 'cursor' in locals():
        cursor.close()
    if 'connection' in locals():
        connection.close()
    print("\nDatabase connection closed cleanly.")


# ─────────────────────────────────────────────
# NODE.JS EXAMPLE — using the 'pg' package
# Save as connect_to_bookshop_db.js and run with: node connect_to_bookshop_db.js
# First install: npm install pg
# ─────────────────────────────────────────────

/*
const { Pool } = require('pg');  // 'Pool' manages a reusable set of connections

// Pool reads from environment variables automatically if you name them correctly.
// Set: DB_HOST, DB_NAME, DB_USER, DB_PASSWORD, DB_PORT in your environment.
const databasePool = new Pool({
  host:     process.env.DB_HOST     || 'localhost',
  database: process.env.DB_NAME     || 'bookshop_db',
  user:     process.env.DB_USER     || 'bookshop_app',
  password: process.env.DB_PASSWORD || '',
  port:     parseInt(process.env.DB_PORT) || 5432,
});

async function fetchAllBooks() {
  let client;

  try {
    // 'connect' borrows a connection from the pool
    client = await databasePool.connect();

    const queryResult = await client.query(
      'SELECT book_id, title, author_name, price FROM books ORDER BY price DESC'
    );

    console.log(`Found ${queryResult.rowCount} books:\n`);

    // queryResult.rows is an array of plain JavaScript objects
    queryResult.rows.forEach(book => {
      console.log(`  [${book.book_id}] ${book.title} by ${book.author_name} — $${book.price}`);
    });

  } catch (queryError) {
    console.error('Database query failed:', queryError.message);
  } finally {
    // Return the connection back to the pool (don't destroy it)
    if (client) client.release();
    await databasePool.end();
  }
}

fetchAllBooks();
*/
▶ Output
Found 3 books in the database:

[3] Designing Data-Intensive Applications by Martin Kleppmann — $49.99
[1] The Pragmatic Programmer by David Thomas — $39.99
[2] Clean Code by Robert Martin — $34.50

Database connection closed cleanly.
⚠️
Security Warning — Never Hardcode Passwords:Putting your database password directly in your source code (like password='mypassword') means it gets committed to Git and is readable by anyone with access to the repo. Always use environment variables or a .env file — and add .env to your .gitignore immediately. This is one of the top causes of real-world data breaches.
Aspectpsql (Command Line)pgAdmin (GUI Tool)
Best forScripting, automation, server environmentsVisual exploration, beginners learning table structure
Comes with PostgreSQLYes, always includedYes, via EnterpriseDB installer (or install separately)
Speed for bulk operationsVery fast — no rendering overheadSlower for large result sets due to UI rendering
Learning curveSteeper — must know psql backslash commandsGentler — click-through interface
Use in productionYes — standard for DBAs and DevOpsRarely — GUI tools aren't used on headless servers
Running SQL scripts\i filename.sql — one commandOpen file via Query Tool, then execute
Seeing table structure\d tablenameClick the table in the left-side object tree

🎯 Key Takeaways

  • PostgreSQL runs as a background server on port 5432 — it must be started before any client (psql, pgAdmin, or your app) can connect to it.
  • Always create a dedicated, limited-privilege database user for your application — never connect with the 'postgres' superuser in production code.
  • In psql, every SQL statement must end with a semicolon. If you see ' ->' PostgreSQL is waiting for it — just type ';' and press Enter.
  • Store database credentials in environment variables, never in source code — hardcoded passwords in Git repos are one of the most common real-world security failures.

⚠ Common Mistakes to Avoid

  • Mistake 1: Using the 'postgres' superuser for your application — Symptom: app works fine locally, but you accidentally have full database admin rights in production, meaning a SQL injection bug could drop every table — Fix: always CREATE USER with a strong password and GRANT only the permissions that specific app needs. The postgres user is for admin tasks only.
  • Mistake 2: Forgetting the semicolon in psql — Symptom: you type a SQL command, press Enter, and see ' ->' instead of a result. psql just sits there waiting. Beginners think it froze — Fix: type a semicolon ';' followed by Enter. psql will immediately execute everything you typed. Every SQL statement in psql must end with ';'.
  • Mistake 3: PostgreSQL server not running when you try to connect — Symptom: you get 'could not connect to server: Connection refused — Is the server running on host localhost and accepting TCP/IP connections on port 5432?' — Fix: start the service first. On Linux: 'sudo systemctl start postgresql'. On macOS: 'brew services start postgresql@16'. On Windows: open Services (services.msc) and start 'postgresql-x64-16'.

Interview Questions on This Topic

  • QWhat is the difference between the 'postgres' superuser and a regular PostgreSQL role, and why would you create a separate user for your application instead of using postgres?
  • QWalk me through what happens step-by-step when your Node.js app calls pool.connect() to connect to a PostgreSQL database — what is a connection pool and why does it matter for performance?
  • QIf your application throws 'FATAL: password authentication failed for user bookshop_app', what are the three most likely causes and how would you debug each one?

Frequently Asked Questions

What is the default port for PostgreSQL and can I change it?

PostgreSQL listens on port 5432 by default. You can change this by editing the 'port' setting in the postgresql.conf file (usually found at /etc/postgresql/16/main/postgresql.conf on Linux), then restarting the service. Changing the port is useful if you're running multiple PostgreSQL instances on the same machine.

What is the difference between a PostgreSQL database and a schema?

A database is the top-level container — you connect to a specific database. A schema is a namespace inside a database that groups tables together. By default, PostgreSQL puts everything in a schema called 'public'. Think of a database as a filing cabinet and a schema as a labelled drawer inside it. Most small projects only ever need the default public schema.

Do I need to install PostgreSQL locally if I'm using a cloud database like AWS RDS or Supabase?

You don't need a local PostgreSQL server to connect to a cloud database — the server lives in the cloud. However, you do need the psql client tools installed locally if you want to connect from your terminal. On macOS and Linux you can install just the client with 'brew install libpq' or 'sudo apt install postgresql-client' without installing the full server.

🔥
TheCodeForge Editorial Team Verified Author

Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.

← PreviousMySQL Installation and SetupNext →MySQL Stored Functions
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged