CREATE DATABASE
Use this lesson to understand CREATE DATABASE with PostgreSQL-native syntax, options, and safe workflows.
Concept Overview
CREATE DATABASE is a Data Definition Language (DDL) command that creates a new database inside a PostgreSQL cluster.
PostgreSQL databases are isolated containers for schemas, tables, and other objects. You connect to exactly one database at a time.
Why is it important?
- Project isolation: keep unrelated apps/environments separated
- Ownership and privileges: decide who can create objects and connect
- Locale/encoding correctness: prevents subtle sorting/character issues later
- Operational safety: sets a clean starting point for migrations and deployments
Where does it fit?
Typical workflow:
- Create database (
CREATE DATABASE) - Connect to it (
\c dbnameinpsql) - Create schemas/tables (
CREATE SCHEMA,CREATE TABLE) - Grant access (roles/privileges)
Important PostgreSQL rule: CREATE DATABASE cannot run inside a transaction block.
Syntax & Rules
Core Syntax
CREATE DATABASE db_name;
With options:
CREATE DATABASE db_name
WITH
OWNER = owner_role
TEMPLATE = template0
ENCODING = 'UTF8'
LC_COLLATE = 'C'
LC_CTYPE = 'C'
CONNECTION LIMIT = 50;
Available Options / Parameters
| Option | Example | Meaning / Notes |
|---|---|---|
OWNER | OWNER = app_owner | database owner (often an admin/migration role) |
TEMPLATE | TEMPLATE = template0 | base database to copy from; template0 is minimal and safe for locale/encoding changes |
ENCODING | ENCODING = 'UTF8' | character encoding; must be compatible with template |
LC_COLLATE | LC_COLLATE = 'en_US.utf8' | collation rules (sorting/comparison) |
LC_CTYPE | LC_CTYPE = 'en_US.utf8' | character classification rules |
TABLESPACE | TABLESPACE = fast_ts | where the database files live (requires privilege) |
CONNECTION LIMIT | CONNECTION LIMIT = 50 | soft limit; -1 means unlimited |
IS_TEMPLATE | IS_TEMPLATE = true | allows use as a template (admin use) |
Key Rules and Considerations
- No
IF NOT EXISTS: PostgreSQL does not supportCREATE DATABASE IF NOT EXISTS. - Must run outside transactions:
CREATE DATABASEerrors insideBEGIN/COMMIT. - Encoding/locale are difficult to change later: choose deliberately at creation time.
- You need
CREATEDBprivilege or be a superuser to create a database. - Database names are identifiers: use lowercase with underscores for consistency.
Connecting and Verifying in psql
\l
\c db_name
Step-by-Step Examples
Example 1: Create a Database (Beginner)
CREATE DATABASE app_db;
Expected output:
CREATE DATABASE
Explanation:
- Creates an empty database named
app_dbusing default options.
Example 2: Create a Database With an Owner and Connection Limit (Intermediate)
CREATE DATABASE app_db
WITH
OWNER = app_owner
CONNECTION LIMIT = 25;
Expected output:
CREATE DATABASE
Explanation:
- Setting
OWNERhelps ensure the correct role owns future objects created by migrations. CONNECTION LIMITcan protect small environments from accidental connection storms.
Example 3: Create a Database With a Specific Encoding/Locale (Advanced)
CREATE DATABASE reports_db
WITH
TEMPLATE = template0
ENCODING = 'UTF8'
LC_COLLATE = 'C'
LC_CTYPE = 'C';
Expected output:
CREATE DATABASE
Explanation:
template0is commonly used when you need non-default locale/encoding settings.- Locale choices affect ordering and text comparisons.
Example 4: Connect and Confirm It Exists (Beginner)
\l
\c app_db
Expected output (example):
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+---------+---------+-------------------
app_db | app_owner| UTF8 | C | C |
postgres | postgres | UTF8 | C | C |
template0 | postgres | UTF8 | C | C | =c/postgres +
template1 | postgres | UTF8 | C | C | =c/postgres +
(4 rows)
You are now connected to database "app_db" as user "postgres".
Explanation:
\llists databases.\cchanges the active connection database inpsql.
Practical Use Cases
1) Create Environment Databases (dev/staging/prod)
Context: isolate environments and permissions.
CREATE DATABASE myapp_dev WITH OWNER = myapp_admin;
CREATE DATABASE myapp_stg WITH OWNER = myapp_admin;
CREATE DATABASE myapp_prod WITH OWNER = myapp_admin;
2) Create a Database for Reporting With Different Collation
Context: maximize stable comparisons/sorts for analytical workloads.
CREATE DATABASE myapp_reports
WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'C' LC_CTYPE = 'C';
3) Limit Connections in Small Shared Instances
Context: avoid accidental exhaustion of connection slots.
CREATE DATABASE sandbox
WITH OWNER = sandbox_owner CONNECTION LIMIT = 10;
4) Create a Template Database (Admin)
Context: seed a pre-configured database for faster provisioning.
CREATE DATABASE my_template WITH IS_TEMPLATE = true;
Common Mistakes & Troubleshooting
Mistake 1: Running Inside a Transaction
Wrong:
BEGIN;
CREATE DATABASE app_db;
COMMIT;
What happens (typical error):
ERROR: CREATE DATABASE cannot run inside a transaction block
Fix: run CREATE DATABASE outside any transaction.
Mistake 2: Expecting IF NOT EXISTS
Wrong:
CREATE DATABASE IF NOT EXISTS app_db;
What happens (typical error):
ERROR: syntax error at or near "IF"
Fix options:
-- Option A: check first
SELECT 1 FROM pg_database WHERE datname = 'app_db';
-- Option B: handle existence at the deployment tool level
Mistake 3: Locale/Encoding Mismatch With the Template
Symptom: ERROR: new encoding ... is incompatible with the encoding of the template database.
Fix: use TEMPLATE = template0 when you need a different locale/encoding.
Debugging tips:
- Confirm privileges: do you have
CREATEDBor superuser? - List existing databases with
\land check encoding/collation. - If creation fails due to template mismatch, switch to
template0. - Standardize database creation in automation so settings are consistent.
Best Practices
- ✅ Decide encoding/collation intentionally; ❌ rely on defaults without understanding locale impact.
- ✅ Use a dedicated owner role for migrations; ❌ create production databases owned by random user accounts.
- ✅ Standardize naming conventions (lowercase, underscores); ❌ mix cases and special characters.
- ✅ Automate DB creation consistently across environments; ❌ create databases manually with drifting options.
- ✅ Keep connection limits sensible for shared instances; ❌ leave unlimited connections on tiny hosts.
Hands-On Practice
Exercise 1 (Easy): Create a database
Task: Create a database named practice_db.
-- Your SQL here
Solution:
CREATE DATABASE practice_db;
Exercise 2 (Medium): Create a database with an owner
Task: Create practice_db2 owned by practice_owner.
-- Your SQL here
Solution:
CREATE DATABASE practice_db2 WITH OWNER = practice_owner;
Exercise 3 (Advanced): Create with template0 and locale
Task: Create practice_reports using template0 and LC_COLLATE = 'C', LC_CTYPE = 'C'.
-- Your SQL here
Solution:
CREATE DATABASE practice_reports
WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'C' LC_CTYPE = 'C';
Connection to Other Concepts
| Concept | Why it matters |
|---|---|
| Roles and privileges | determine who can connect/create objects |
| Schemas | organize objects inside a database |
| CREATE TABLE | next step after database creation |
| Backup and recovery | database creation is the start of lifecycle management |
| Transactions | some DDL cannot run inside a transaction in PostgreSQL |
Visual Learning Diagram
flowchart TD
A[PostgreSQL Cluster] --> B[CREATE DATABASE]
B --> C[Database: app_db]
C --> D[Schemas]
D --> E[Tables / Views / Functions]
C --> F[Roles + Privileges]
C --> G[Encoding + Collation]
classDef allNodes fill:#3e3e3e,stroke:#ffffff,stroke-width:2px,color:#f5f5f5
classDef highlight fill:#3e3e3e,stroke:#ffffff,stroke-width:4px,color:#f5f5f5
class A,B,C,D,E,F,G allNodes
class B highlight
Common Pitfalls
| Pitfall | Consequence | Prevention |
|---|---|---|
| Creating with the wrong locale | unexpected sort/comparison behavior | standardize locale at creation |
Assuming IF NOT EXISTS exists | deployment failures | check pg_database or handle in tooling |
| Running in a transaction | immediate error | run outside BEGIN/COMMIT |
| Wrong owner | privilege pain later | set OWNER explicitly |
| Manual one-off creation | drifting settings | automate database provisioning |
Quick Reference
CREATE DATABASE app_db;
CREATE DATABASE app_db WITH OWNER = app_owner;
CREATE DATABASE reports_db WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'C' LC_CTYPE = 'C';
\l
\c app_db
What's Next
- Next: DROP DATABASE - Learn safe deletion patterns and how to handle active connections.
- Module Overview - Return to the Database Management index.