Database Postgres
Database container for PlanExe. Used as a queue mechanism for planning tasks. The worker_plan_database listens for an incoming task, and runs PlanExe and then goes back to listen for more incoming tasks.
In a single user environment, then this is overkill. The file system is sufficient.
In a multi user environment, then there are many moving parts, and here a database is relevant.
- Build/run via
docker compose up database_postgres(ordocker compose build database_postgres). - Defaults:
PLANEXE_POSTGRES_USER=planexe,PLANEXE_POSTGRES_PASSWORD=planexe,PLANEXE_POSTGRES_DB=planexe(override with env or.env). - Ports:
${PLANEXE_POSTGRES_PORT:-5432}on the host mapped to5432in the container. SetPLANEXE_POSTGRES_PORTin.envor your shell to avoid clashes. - Data: persisted in the named volume
database_postgres_data.
Choose a host port
The default PostgreSQL port is 5432. On developer machines, this port is often already occupied by a local PostgreSQL installation:
- macOS: Postgres.app (a popular menu-bar Postgres that auto-starts), Homebrew PostgreSQL (
brew install postgresql), or pgAdmin's bundled server - Linux: System PostgreSQL installed via
apt install postgresql,dnf install postgresql-server, etc. - Windows: PostgreSQL installer, pgAdmin, or other database tools
If port 5432 is in use, Docker will fail to start database_postgres with a "port already in use" error.
Solution: Set PLANEXE_POSTGRES_PORT to a different value before starting the container:
Or add it to your .env file to make it permanent:
Replace 5433 with any free host port you prefer.
Important: This only affects the HOST port mapping (how you access Postgres from your machine). Inside Docker, containers always communicate with each other on the internal port 5432—this is hardcoded and not affected by PLANEXE_POSTGRES_PORT.
Verify the container
- Check status:
docker compose ps database_postgres - Shell in to confirm Postgres is the right one:
docker compose exec database_postgres psql -U planexe -d planexe
DBeaver
For managing the database, I recommend using the DBeaver Community app, which is open source.
https://github.com/dbeaver/dbeaver
Connect with host localhost, port ${PLANEXE_POSTGRES_PORT:-5432}, database planexe, user planexe, password planexe (or whatever you set in .env).
Railway + DBeaver
DBeaver cannot connect via the Railway CLI tunnel (railway ssh/connect), because the CLI does not provide a traditional TCP port forward. Instead, use Railway's TCP Proxy feature.
1. Enable TCP Proxy in Railway
- Go to your Railway dashboard →
database_postgresservice - Navigate to Settings → Networking → Public Networking
- Add a TCP Proxy with port
5432 - Railway will assign a hostname and port, e.g.,
subsubdomain.subdomain.example.com:12345
Warning: Only enable TCP Proxy after setting a secure password (see below).
Warning: The TCP Proxy connection is unencrypted. Railway's TCP Proxy forwards raw TCP traffic without adding TLS, and the
postgres:16-alpineimage doesn't have SSL enabled by default. Your password and data travel in plain text. Consider disabling TCP Proxy when not in use, or configure SSL on the PostgreSQL container for production use.
2. Set a secure password
The default password planexe is too easy to guess. PostgreSQL only sets the password on first initialization, so if the database already exists:
- Connect with the current password
- Run:
ALTER USER planexe WITH PASSWORD 'your-secure-password'; - Update
POSTGRES_PASSWORDin Railway's environment variables to match
3. Connect with DBeaver
In DBeaver, create a new PostgreSQL connection with "Connect by: Host":
| Field | Value |
|---|---|
| Host | Your TCP Proxy hostname (e.g., subsubdomain.subdomain.example.com) |
| Port | Your assigned port (e.g., 12345, NOT 5432) |
| Database | planexe |
| Username | planexe |
| Password | Your secure password |
Click Test Connection to verify.
4. Security check
Try connecting with password planexe. If it succeeds, the password hasn't been changed yet—go back to step 2.
See railway.md for more details.
SSL (Future Plan)
The current setup uses unencrypted connections. For production use with public TCP Proxy exposure, SSL/TLS should be enabled to encrypt traffic between clients and the database.
What's needed
1. Generate SSL certificates
You'll need a certificate and private key. Options: - Self-signed: Quick for internal use, but clients must trust the certificate manually - Let's Encrypt: Free, but requires domain validation (complex for raw TCP) - Commercial CA: Trusted by default, but costs money
Example self-signed certificate generation:
openssl req -new -x509 -days 365 -nodes \
-out server.crt \
-keyout server.key \
-subj "/CN=database_postgres"
2. Update the Dockerfile
Add the certificates and configure PostgreSQL to use them:
FROM postgres:16-alpine
# ... existing ENV statements ...
# Copy SSL certificates
COPY server.crt /var/lib/postgresql/server.crt
COPY server.key /var/lib/postgresql/server.key
# Set correct permissions (required by PostgreSQL)
RUN chmod 600 /var/lib/postgresql/server.key && \
chown postgres:postgres /var/lib/postgresql/server.crt /var/lib/postgresql/server.key
# Enable SSL in PostgreSQL
RUN echo "ssl = on" >> /usr/local/share/postgresql/postgresql.conf.sample && \
echo "ssl_cert_file = '/var/lib/postgresql/server.crt'" >> /usr/local/share/postgresql/postgresql.conf.sample && \
echo "ssl_key_file = '/var/lib/postgresql/server.key'" >> /usr/local/share/postgresql/postgresql.conf.sample
3. Configure DBeaver for SSL
In DBeaver's connection settings:
- Go to the SSL tab
- Check "Use SSL"
- Set SSL mode:
require— Encrypt connection, don't verify certificateverify-ca— Encrypt and verify certificate against a CAverify-full— Encrypt, verify certificate, and check hostname- For self-signed certs, you may need to import the CA/certificate or set "Trust all certificates"
4. Enforce SSL on the server (optional)
To reject unencrypted connections, add to pg_hba.conf:
Resources
Railway backup to local file
Use database_postgres/download_backup.py to stream a compressed dump from the Railway database_postgres service to your machine.
Prereq: Railway CLI installed and logged in.
- Runs
railway link(skip with--skip-linkif already linked). - Streams
pg_dump -F c -Z9viarailway sshand writesYYYYMMDD-HHMM.dumpin the current directory. - Options:
--userPostgres user (default:$PLANEXE_POSTGRES_USERorplanexe)--dbPostgres database (default:$PLANEXE_POSTGRES_DBorplanexe)--output-dir pathDirectory for the dump file--filename name.dumpOverride dump filename--service other_serviceRailway service name--skip-linkSkiprailway linkif already linked
Restore a backup locally
Run a Postgres you can reach (for example docker compose up database_postgres on your machine), then restore the custom-format dump:
PGPASSWORD=planexe pg_restore \
-h localhost \
-p 5432 \
-U planexe \
-d planexe \
/path/to/19841231-2359.dump
- The dump is custom format (
pg_dump -F c), so usepg_restore, notpsql. - Ensure the target database exists; add
-cto drop objects before recreating them if you want a clean restore. - If you changed credentials/DB name in
.envor Railway, use those here.