PostgreSQL Read-Only User
Create a read-only database user for reporting, BI tools, and analytics without risking data modification.
Why Use a Read-Only User?
| Benefit | Description |
|---|---|
| Security | Prevents accidental data modification |
| Compliance | Separate credentials for external tools |
| Auditing | Track who accesses data for reporting |
| Performance | Can connect to read replica (Odoo 18+) |
Read Replicas: If you're using OEC.sh's PostgreSQL read replica feature (Odoo 18+), connect your BI tools to the replica instead of the primary database for better performance.
Create Read-Only User
Access the Terminal
- Go to Environments in OEC.sh
- Click on your environment
- Click Terminal button
Connect to PostgreSQL
# Connect to the database container
docker exec -it YOUR_ENV_ID_db psql -U odoo YOUR_DATABASE_NAMEReplace:
YOUR_ENV_ID_dbwith your database container nameYOUR_DATABASE_NAMEwith your Odoo database name
Create the User
-- Create read-only user
CREATE USER readonly_user WITH PASSWORD 'secure_password_here';
-- Grant connect permission
GRANT CONNECT ON DATABASE your_database_name TO readonly_user;
-- Grant schema usage
GRANT USAGE ON SCHEMA public TO readonly_user;
-- Grant SELECT on all existing tables
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;
-- Grant SELECT on all future tables (important!)
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO readonly_user;
-- Grant SELECT on sequences (for auto-increment columns)
GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO readonly_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON SEQUENCES TO readonly_user;Verify Access
-- List user permissions
\du readonly_user
-- Test as readonly user (exit and reconnect)
\qThen reconnect as the read-only user:
psql -U readonly_user -d your_database_name -h localhostTest Restrictions
-- This should work
SELECT COUNT(*) FROM res_partner;
-- This should fail with "permission denied"
DELETE FROM res_partner WHERE id = 1;Connection Details for BI Tools
Connection String
postgresql://readonly_user:your_password@server-ip:5432/database_nameCommon BI Tool Settings
| Setting | Value |
|---|---|
| Host | Your server IP or domain |
| Port | 5432 (or 6432 for PgBouncer) |
| Database | Your Odoo database name |
| Username | readonly_user |
| Password | The password you set |
| SSL | Required for production |
Using PgBouncer (Recommended)
If PgBouncer is enabled:
| Connection Type | Port |
|---|---|
| Primary database | 6432 |
| Read replica (Odoo 18+) | 6433 |
Port 5432 Access: By default, PostgreSQL port is not exposed externally. You may need to:
- Open port 5432 in your firewall (see Firewall & Ports), or
- Use an SSH tunnel for secure access
SSH Tunnel (Recommended for Security)
Instead of exposing PostgreSQL to the internet, use an SSH tunnel:
Create Tunnel
# On your local machine or BI server
ssh -L 5432:localhost:5432 user@your-oecsh-server -NConnect via Tunnel
Your BI tool connects to:
- Host:
localhost - Port:
5432 - (The tunnel forwards to the remote PostgreSQL)
Persistent Tunnel with autossh
# Install autossh
apt install autossh
# Create persistent tunnel
autossh -M 0 -f -N -L 5432:localhost:5432 user@your-oecsh-serverConnecting Popular BI Tools
Metabase
- Go to Admin → Databases → Add database
- Select PostgreSQL
- Enter connection details:
- Display name:
Odoo Production (Read-Only) - Host: Your server IP
- Port:
5432 - Database: Your database name
- Username:
readonly_user - Password: Your password
- Display name:
- Click Save
Power BI
- Get Data → PostgreSQL database
- Server:
your-server-ip:5432 - Database: Your database name
- Data Connectivity: Import
- Enter
readonly_usercredentials
Tableau
- Connect → PostgreSQL
- Server: Your server IP
- Port:
5432 - Database: Your database name
- Username:
readonly_user - Authentication: Username and Password
- Require SSL: Recommended for production
Grafana
- Configuration → Data Sources → Add data source
- Select PostgreSQL
- Host:
your-server-ip:5432 - Database: Your database name
- User:
readonly_user - Password: Your password
- SSL Mode:
require(recommended) - Click Save & Test
Restrict Access to Specific Tables
For additional security, you can limit which tables the user can access:
-- Revoke access to all tables first
REVOKE SELECT ON ALL TABLES IN SCHEMA public FROM readonly_user;
-- Grant access only to specific tables
GRANT SELECT ON TABLE res_partner TO readonly_user;
GRANT SELECT ON TABLE sale_order TO readonly_user;
GRANT SELECT ON TABLE sale_order_line TO readonly_user;
GRANT SELECT ON TABLE account_move TO readonly_user;
GRANT SELECT ON TABLE account_move_line TO readonly_user;
GRANT SELECT ON TABLE product_product TO readonly_user;
GRANT SELECT ON TABLE product_template TO readonly_user;Row-Level Security (Advanced)
For multi-company setups, restrict access to specific company data:
-- Enable row-level security on a table
ALTER TABLE sale_order ENABLE ROW LEVEL SECURITY;
-- Create policy for specific company
CREATE POLICY company_isolation ON sale_order
FOR SELECT
TO readonly_user
USING (company_id = 1);
-- Force the policy even for table owner
ALTER TABLE sale_order FORCE ROW LEVEL SECURITY;Odoo Compatibility: Row-level security can interfere with Odoo's own access rules. Test thoroughly in a staging environment first.
Common Tables for Reporting
Sales & CRM
| Table | Description |
|---|---|
crm_lead | Leads and opportunities |
sale_order | Sales orders |
sale_order_line | Sales order lines |
res_partner | Customers and contacts |
Accounting
| Table | Description |
|---|---|
account_move | Invoices and journal entries |
account_move_line | Invoice/journal lines |
account_payment | Payments |
account_account | Chart of accounts |
Inventory
| Table | Description |
|---|---|
stock_move | Stock movements |
stock_quant | Stock quantities |
stock_picking | Delivery orders |
product_product | Product variants |
HR
| Table | Description |
|---|---|
hr_employee | Employees |
hr_leave | Time off requests |
hr_attendance | Attendance records |
Security Best Practices
- Use strong passwords - At least 16 characters, random
- Rotate credentials - Change passwords periodically
- Use SSL - Always require SSL for remote connections
- Limit IP access - Restrict in firewall to BI server IPs only
- Audit access - Enable PostgreSQL logging for the user
- Use read replica - Connect BI tools to replica, not primary (Odoo 18+)
- SSH tunnel - Prefer SSH tunnel over exposing port 5432
Troubleshooting
"Permission denied for table"
Cause: User doesn't have SELECT permission on the table.
Solution:
GRANT SELECT ON TABLE table_name TO readonly_user;"Connection refused"
Cause: PostgreSQL not accepting external connections.
Solution:
- Check
postgresql.conf:listen_addresses = '*' - Check
pg_hba.conffor the user's IP - Check firewall allows port 5432
"Password authentication failed"
Cause: Wrong password or user doesn't exist.
Solution:
-- Verify user exists
SELECT * FROM pg_user WHERE usename = 'readonly_user';
-- Reset password if needed
ALTER USER readonly_user WITH PASSWORD 'new_password';Query Performance Issues
Cause: Large queries affecting production database.
Solutions:
- Use read replica instead of primary (Odoo 18+)
- Add indexes for frequently queried columns
- Limit query time:
SET statement_timeout = '30s'; - Schedule reports during off-peak hours
Revoking Access
To remove the read-only user:
-- Revoke all permissions
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM readonly_user;
REVOKE ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public FROM readonly_user;
REVOKE CONNECT ON DATABASE your_database FROM readonly_user;
-- Drop the user
DROP USER readonly_user;Need Help?
For database access configuration:
- Email: [email protected]
- Include: Odoo version, BI tool name, connection errors