Short answer
To host Odoo on OVHcloud, pick a VPS or a Public Cloud instance with enough memory. Put Postgres on the same server, on a second instance in a private network, or on OVHcloud's managed PostgreSQL. Open only ports 80 and 443 to the world, and SSH only to your own address. Send a nightly copy of the database and the filestore to an Object Storage bucket in another region, with a user that can only write to that bucket. Then restore one copy on another server before you go live.
OVHcloud is a French company with data centers in Europe and beyond. Many teams pick it to keep their data with a European provider. This guide gives you the exact commands for each step on OVHcloud.
For the install itself (Odoo, Postgres, the config file and the proxy), follow our guide on running Odoo on a VPS. This page covers what's different on OVHcloud.
VPS or Public Cloud
OVHcloud sells two kinds of server that suit Odoo:
- VPS. A fixed size, a simple control panel, and a daily backup included. Good for one Odoo server with Postgres on it.
- Public Cloud instances. Many sizes, private networks (vRack), security groups, and a command line tool (OpenStack CLI). Better when you want Postgres on its own server or on the managed service.
Minimum to start: 2 vCPU, 4 GB of memory and 50 GB of disk. That runs Odoo and Postgres on one server for a small team. Memory matters more than CPU: each worker needs its share, and Postgres wants its own. For more users, big imports or Postgres on its own server, our guide on how many Odoo workers you need shows how to size it.
Pick the region
Your data lives where the server lives. Put it close to your users.
- On Public Cloud, a region can be 1-AZ (one data center, like Gravelines
GRAor StrasbourgSBG), 3-AZ (three zones, like ParisEU-WEST-PAR), or a Local Zone. - Avoid Local Zones for Odoo. They can't join a vRack, they can't send email on any SMTP port, and a Local Zone VPS has no snapshots, no automated backups and no Anti-DDoS.
Create a Public Cloud instance from the command line
First, install the OpenStack client on your own computer:
sudo apt install -y python3-pip python3-venv
python3 -m venv ~/openstack-env
source ~/openstack-env/bin/activate
pip install --upgrade pip
pip install python-openstackclient
Then get your login file. In the control panel, open your Public Cloud project. Go to Settings > Users & Roles and click Create User. Give it the Administrator role and save the password it shows you. It is shown only once. Then click ... next to the user and choose Download OpenStack's RC file. Each file is for one region.
source ~/Downloads/openrc.sh # asks for the user's password
openstack server list # works if your login is right
Add your SSH key, then look up a size (flavor) and an image:
openstack keypair create --public-key ~/.ssh/id_ed25519.pub my-key
openstack flavor list --min-ram 7000
openstack image list --property visibility=public | grep -i ubuntu
Flavor names are like b2-7 (General Purpose, 7 GB of memory). Pick one by the memory you need. Then create the instance. We add a security group here. The next section shows how to make it.
openstack server create \
--flavor b2-7 \
--image "Ubuntu 24.04" \
--key-name my-key \
--network Ext-Net \
--security-group odoo-web \
odoo-web
Ext-Net is OVHcloud's public network. It gives the instance a public IP. Check that the instance is up and read its address:
openstack server show odoo-web -c status -c addresses -c security_groups
ssh ubuntu@<public-ip>
The user name follows the image: ubuntu on Ubuntu, debian on Debian.
Create a VPS
A VPS has no OpenStack CLI. You order it in the control panel and pick the OS and your SSH key there. To add a key later, go to Bare Metal Cloud > Virtual private servers, pick your VPS, click ... next to OS/Distribution, then Reinstall my VPS. Choose your key under SSH Key to pre-install. This wipes the disks, so do it before you install anything.
Log in with ssh ubuntu@<vps-ip> on Ubuntu, or debian@ on Debian.
Where Postgres goes
You have three choices.
1. On the same server
This is the simplest. It is fine for many teams. The VPS guide covers it. Postgres listens only on localhost, so there is nothing to open.
2. On a second instance, over a private network
On Public Cloud, you can put Postgres on its own instance and talk to it over a private network. New projects come with a vRack, so you only need to create the network.
openstack network create odoo-private
openstack subnet create odoo-private-subnet \
--network odoo-private \
--subnet-range 10.1.0.0/24 \
--dhcp --gateway none
--gateway none stops the private network from sending a second default route to your servers. Their internet traffic keeps going through Ext-Net.
Create both servers with both networks. Give the database server its own security group (see the firewall section):
openstack server create --flavor b2-7 --image "Ubuntu 24.04" --key-name my-key \
--network Ext-Net --network odoo-private --security-group odoo-web odoo-web
openstack server create --flavor b2-15 --image "Ubuntu 24.04" --key-name my-key \
--network Ext-Net --network odoo-private --security-group odoo-db odoo-db
If the Odoo server already exists, attach it with openstack server add network odoo-web odoo-private. Then reboot it once and check that its network names did not swap.
Find the private address of each server:
openstack server show odoo-db -c addresses
# addresses | Ext-Net=..., 51.x.x.x ; odoo-private=10.1.0.12
On the database server, make Postgres listen on its private address too. In /etc/postgresql/16/main/postgresql.conf (change 16 to your version):
listen_addresses = 'localhost,10.1.0.12'
In /etc/postgresql/16/main/pg_hba.conf, let only the odoo role in, only from the private network, and only to yourdb and postgres. Odoo's live chat connects to the postgres database, so leave it in the list:
hostssl yourdb,postgres odoo 10.1.0.0/24 scram-sha-256
Restart and test from the Odoo server:
sudo systemctl restart postgresql # on odoo-db
psql "host=10.1.0.12 dbname=yourdb user=odoo sslmode=require" -c "select 1" # on odoo-web
Then in odoo.conf on the Odoo server, set db_host = 10.1.0.12 and db_sslmode = require.
3. On OVHcloud managed PostgreSQL
OVHcloud runs the database server for you: updates, failover and backups.
Create the service. In your Public Cloud project, go to Databases and click Create a service. Pick PostgreSQL and a version your Odoo release supports. Pick the same region as your Odoo server, then a plan and a size. Under network, you can choose a private network (vRack) if your Odoo server is on Public Cloud.
Allow your Odoo server. By default, the service accepts no connections at all. It even looks ready while it refuses everyone. Open the Configuration tab, find IPs, and add your Odoo server's public IP as /32 (for example 203.0.113.10/32). On a private network, add your private subnet instead. Never add 0.0.0.0/0 in production.
Get the admin login. The first user is called avnadmin. Open the Users tab, click ... next to it, and choose Reset password. Save the password now. OVHcloud does not keep it. The Dashboard tab shows the host name and the port. The port is not 5432. It looks like 20184.
Download the CA certificate. On the Dashboard tab, the Connection information section has the certificate. Save it on the Odoo server as /etc/odoo/ovh-db-ca.pem.
Create the odoo user and the database. avnadmin can create users and databases, but it is not a superuser. So it must first become a member of the odoo role before it can make odoo the owner of a database. Connect from the Odoo server:
psql "postgres://avnadmin@<host>:<port>/defaultdb?sslmode=verify-full&sslrootcert=/etc/odoo/ovh-db-ca.pem"
CREATE ROLE odoo LOGIN;
\password odoo
GRANT odoo TO avnadmin;
CREATE DATABASE yourdb OWNER odoo ENCODING 'UTF8';
\l yourdb
Users you create in SQL don't show up in the control panel's Users tab. That's normal. odoo owns its database, so Odoo can work in it. It can't create other databases, and that's what you want.
Point Odoo at it. In odoo.conf:
db_host = postgresql-xxxxxxxx-xxxxxxxx.database.cloud.ovh.net
db_port = 20184
db_user = odoo
db_password = <odoo password>
db_name = yourdb
dbfilter = ^yourdb$
db_sslmode = require
require encrypts the link. To also check that you are talking to the right server, use db_sslmode = verify-full. Then tell the Odoo service where the certificate is with sudo systemctl edit odoo:
[Service]
Environment=PGSSLROOTCERT=/etc/odoo/ovh-db-ca.pem
Always use the host name from the control panel, never the IP. The IP can change, and verify-full fails on an IP.
Watch the connection limit. The service allows about 100 connections per 4 GB of memory. Each Odoo worker can open up to db_maxconn connections (64 by default). Keep (workers + max_cron_threads + 1) × db_maxconn under your limit. For example, set db_maxconn = 16.
What its backups cover. The managed service backs up the database all the time. You can restore it to any point in time within your plan's retention. A copy goes to a second region (for example, from GRA to SBG). It does not back up the filestore. Attachments and images still live on the Odoo server, in /var/lib/odoo/filestore. You back those up yourself, below.
Firewall
Allow only this from the internet:
- 80 and 443 from anywhere
- 22 only from your own IP addresses
Block everything else. Odoo's own ports (8069 and 8072) must only be reached by the proxy on the server itself.
Public Cloud: security groups
The default security group lets everything in. Don't change it or delete it. OVHcloud's own tools reset it. Create your own group, and give it to the instance instead of default.
openstack security group create odoo-web --description "Odoo web server"
openstack security group rule create --ingress --protocol tcp --dst-port 80 --remote-ip 0.0.0.0/0 odoo-web
openstack security group rule create --ingress --protocol tcp --dst-port 443 --remote-ip 0.0.0.0/0 odoo-web
openstack security group rule create --ingress --ethertype IPv6 --protocol tcp --dst-port 80 --remote-ip ::/0 odoo-web
openstack security group rule create --ingress --ethertype IPv6 --protocol tcp --dst-port 443 --remote-ip ::/0 odoo-web
openstack security group rule create --ingress --protocol tcp --dst-port 22 --remote-ip 198.51.100.7/32 odoo-web
Replace 198.51.100.7 with your office or VPN address. A new group already lets all outgoing traffic out, and security groups remember open connections. So replies to your server's own requests come back in on their own.
For a database server on the private network, allow Postgres only from servers in the odoo-web group:
openstack security group create odoo-db --description "Postgres server"
openstack security group rule create --ingress --protocol tcp --dst-port 5432 --remote-group odoo-web odoo-db
openstack security group rule create --ingress --protocol tcp --dst-port 22 --remote-ip 198.51.100.7/32 odoo-db
If you created the server with default, swap the groups:
openstack server add security group odoo-web odoo-web
openstack server remove security group odoo-web default
openstack server show odoo-web -c security_groups
Then test from your laptop. Port 443 should answer and 8069 should not:
nc -zv -w 5 <public-ip> 443
nc -zv -w 5 <public-ip> 8069 # must time out
VPS: Edge Network Firewall
A VPS has no security groups. OVHcloud gives you the Edge Network Firewall instead. It filters traffic at the edge of OVHcloud's network, before it reaches your server. Find it in the control panel under Network > Public IP Addresses. Click ... next to your VPS's IPv4 address, then Configure Edge Network Firewall.
Three things to know before you add rules:
- It is stateless. It does not remember connections. If you don't allow replies, your server can't download updates or reach Object Storage. Rule 0 below fixes that.
- Order matters. Rules run from priority 0 to 19. The first rule that matches wins. The last rule must deny the rest. A list with only "Accept" rules blocks nothing.
- It only covers IPv4, and only traffic from outside OVHcloud. You still need a firewall on the server.
Add these rules (up to 20 per IP):
| Priority | Action | Protocol | Source IP | Destination port | Other |
|---|---|---|---|---|---|
| 0 | Accept | TCP | TCP state: established | ||
| 1 | Accept | ICMP | |||
| 2 | Accept | TCP | your IP/32 | 22 | |
| 3 | Accept | TCP | 80 | ||
| 4 | Accept | TCP | 443 | ||
| 5 | Accept | UDP | Source port 53 (DNS replies) | ||
| 6 | Accept | UDP | Source port 123 (time sync replies) | ||
| 19 | Deny | IPv4 |
Leave the source port empty on rules 2 to 4. The client picks a random one. Then turn the firewall on with the switch.
Even if you leave it off, OVHcloud turns it on by itself during a DDoS attack, with your rules. So keep the rules right either way. You can use the same firewall on a Public Cloud instance's public IP too, but security groups already do the job there.
On the server: ufw
On both a VPS and an instance, also turn on the firewall inside Ubuntu. Allow SSH first, so you don't lock yourself out:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 198.51.100.7 to any port 22 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
On a separate database server, allow Postgres only from the private network: sudo ufw allow from 10.1.0.0/24 to any port 5432 proto tcp.
Backups to Object Storage
What OVHcloud backs up for you
- VPS automated backup. One daily backup of the whole server comes with a VPS. The premium option keeps the last 7 days. It does not include extra disks.
- VPS snapshot. A paid option. You keep one snapshot at a time, and restoring it deletes it. Use it before an upgrade.
- Public Cloud instance backups. One-off, or a daily schedule that keeps the last 7 or 14. You can add a copy in another region. From the CLI:
openstack server image create --name odoo-web-before-upgrade odoo-web.
All of these copy the whole disk while Postgres is running. They're useful to rebuild a server. They're not enough on their own for Odoo. You also need a clean copy of the database and the filestore, taken together, kept off the server, and tested. If you do use VPS backups, install the QEMU agent so the disk is frozen cleanly during the copy:
sudo apt install -y qemu-guest-agent
sudo systemctl start qemu-guest-agent
Create the bucket and a backup-only user
Pick a bucket region different from your server. For example, server in GRA, bucket in SBG. Endpoints look like https://s3.<region>.io.cloud.ovh.net, so https://s3.sbg.io.cloud.ovh.net for Strasbourg.
You need two Object Storage users. Here's why. OVHcloud's user policies can't limit the user who owns a bucket. The owner can always do everything.
- Owner user. In your Public Cloud project, open Object Storage and create the bucket
your-odoo-backups. Turn on versioning in the form. Link a new user to it. Keep this user's keys on your own computer, never on the Odoo server. - Backup user. In the Object Storage users list, click Create User and save its access key and secret key. Click ... next to it, choose Import JSON file, and upload this policy:
{
"Statement": [
{
"Sid": "OdooBackupWriteOnly",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:ListBucketMultipartUploads",
"s3:ListMultipartUploadParts",
"s3:AbortMultipartUpload"
],
"Resource": [
"arn:aws:s3:::your-odoo-backups",
"arn:aws:s3:::your-odoo-backups/*"
]
},
{
"Sid": "HideOtherBuckets",
"Effect": "Deny",
"Action": ["s3:ListAllMyBuckets"],
"Resource": ["*"]
}
]
}
This user can add and read backups in that one bucket. It can't delete them, and it can't see other buckets. If someone breaks into the Odoo server, they can't wipe your backups. Versioning also keeps the old copy if a file is overwritten.
Versioning and expiry
Old backups should go away on their own. Set this from your own computer with the owner user's keys, after versioning is on. Save it as lifecycle.json:
{
"Rules": [
{
"ID": "expire-odoo-backups",
"Status": "Enabled",
"Filter": { "Prefix": "" },
"Expiration": { "Days": 30 },
"NoncurrentVersionExpiration": { "NoncurrentDays": 7 },
"AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 3 }
},
{
"ID": "clean-delete-markers",
"Status": "Enabled",
"Filter": { "Prefix": "" },
"Expiration": { "ExpiredObjectDeleteMarker": true }
}
]
}
aws s3api get-bucket-versioning --bucket your-odoo-backups --profile ovh-owner
aws s3api put-bucket-lifecycle-configuration --bucket your-odoo-backups \
--lifecycle-configuration file://lifecycle.json --profile ovh-owner
aws s3api get-bucket-lifecycle-configuration --bucket your-odoo-backups --profile ovh-owner
Backups older than 30 days are removed, and replaced versions go 7 days later. OVHcloud runs these rules once a day or so, not to the minute. Turn versioning on first: OVHcloud won't accept a lifecycle rule while versioning is paused.
Set up the AWS CLI on the server
sudo snap install aws-cli --classic
sudo mkdir -p /backups && sudo chown odoo: /backups && sudo chmod 700 /backups
sudo -u odoo -H aws configure --profile ovh
# Access key and secret key: the backup user's keys
# Default region name: sbg
# Default output format: json
sudo -u odoo -H aws configure set endpoint_url https://s3.sbg.io.cloud.ovh.net --profile ovh
The odoo user's ~/.aws/config now looks like this:
[profile ovh]
region = sbg
output = json
endpoint_url = https://s3.sbg.io.cloud.ovh.net
Test it, and check that delete is refused:
echo test | sudo -u odoo -H aws s3 cp - s3://your-odoo-backups/test.txt --profile ovh
sudo -u odoo -H aws s3 ls s3://your-odoo-backups/ --profile ovh
sudo -u odoo -H aws s3 rm s3://your-odoo-backups/test.txt --profile ovh # must fail: AccessDenied
If uploads fail with an "invalid argument" error on a recent AWS CLI, add these two lines under [profile ovh]:
request_checksum_calculation = when_required
response_checksum_validation = when_required
The backup script
#!/bin/bash
# /usr/local/bin/odoo-backup.sh, run as the odoo user
set -euo pipefail
DB=yourdb
DAY=$(date +%F)
DIR=/backups/$DAY
mkdir -p "$DIR"
pg_dump -Fc "$DB" > "$DIR/$DB.dump"
tar -czf "$DIR/$DB-filestore.tar.gz" -C /var/lib/odoo/filestore "$DB"
aws s3 cp "$DIR" "s3://your-odoo-backups/$DAY/" --recursive --profile ovh
find /backups -mindepth 1 -maxdepth 1 -type d -mtime +3 -exec rm -rf {} +
sudo chmod 755 /usr/local/bin/odoo-backup.sh
With managed PostgreSQL or a second server, pg_dump must reach the remote database. Change the dump line to:
pg_dump -h postgresql-xxxxxxxx-xxxxxxxx.database.cloud.ovh.net -p 20184 -U odoo -Fc "$DB" > "$DIR/$DB.dump"
Add export PGSSLMODE=require near the top of the script. Store the password in the odoo user's ~/.pgpass, with chmod 600:
postgresql-xxxxxxxx-xxxxxxxx.database.cloud.ovh.net:20184:*:odoo:<odoo password>
Your pg_dump must be the same major version as the server or newer. Ubuntu's own client may be older than the managed service. Install the matching client from the PostgreSQL repository:
sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
sudo apt install -y postgresql-client-17 # match your server's version
Schedule it with systemd
/etc/systemd/system/odoo-backup.service:
[Unit]
Description=Back up Odoo database and filestore to OVHcloud Object Storage
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
User=odoo
ExecStart=/usr/local/bin/odoo-backup.sh
/etc/systemd/system/odoo-backup.timer:
[Unit]
Description=Nightly Odoo backup
[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=15m
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now odoo-backup.timer
sudo systemctl start odoo-backup.service # run once now
journalctl -u odoo-backup.service -n 50 # read the result
systemctl list-timers odoo-backup.timer # next run
Test a restore
Do this on a second server, not on production. First, download one day's backup:
aws s3 cp s3://your-odoo-backups/2026-10-26/ ./restore/ --recursive --profile ovh
cd restore
Then restore it:
createdb -O odoo yourdb_restored
pg_restore -d yourdb_restored --no-owner --role=odoo yourdb.dump
mkdir -p /var/lib/odoo/filestore/yourdb_restored
tar -xzf yourdb-filestore.tar.gz -C /var/lib/odoo/filestore/yourdb_restored --strip-components=1
On managed PostgreSQL, odoo can't create databases. Create it as avnadmin in psql with CREATE DATABASE yourdb_restored OWNER odoo;. Then run pg_restore with -h, -p and -U odoo. Make sure the restore server's IP is allowed in the IPs list.
Before you start Odoo on the copy, neutralize it. A copy of production still has your mail servers and scheduled jobs, so it can email real customers. As the odoo user, run:
odoo neutralize -c /etc/odoo/odoo.conf -d yourdb_restored
Use odoo-bin instead of odoo if you run Odoo from source. This turns off outgoing mail servers, scheduled actions and payment providers in the copy. Our guide on copying production to staging explains what it covers and what it misses.
Start Odoo on the restored database. Log in, open a few records with attachments, and check that the files open. Our guide on how to know if an Odoo backup will restore covers what to check.
Monitoring and alerts
What OVHcloud watches for you:
- Managed PostgreSQL emails you when its disk gets close to full. If it fills, the service switches to read-only, and Odoo stops saving.
- Anti-DDoS is on for every OVHcloud service by default.
Inside your server, the disk and Odoo itself are yours to watch. Two simple checks cover most problems:
- Uptime. Point any outside uptime checker at
https://your-odoo-domain/web/login, with an alert by email or text. - Disk. Use a "heartbeat" check from the same service: it alerts you when a ping stops coming. This script pings only while the disk is below 80% full. So you hear about a full disk and a dead server the same way.
#!/bin/bash
# /usr/local/bin/disk-check.sh
USED=$(df --output=pcent / | tail -1 | tr -dc '0-9')
if [ "$USED" -lt 80 ]; then
curl -fsS -m 10 --retry 3 "https://your-heartbeat-url" > /dev/null
fi
Run it every 10 minutes with a systemd timer, built like the backup one, with OnCalendar=*:0/10. Also check /var/lib/odoo and /backups if they are on other disks. Rotate the Odoo log with logrotate so it can't fill the disk.
Sending email from Odoo
On Public Cloud instances, outgoing port 25 is blocked by default. On a VPS, it is open. In Local Zones, every SMTP port is closed for good.
Don't send mail straight from the server either way. In Odoo, set up an outgoing mail server that uses your email provider on port 587 (STARTTLS) or 465 (SSL). Then add SPF, DKIM and DMARC records for your domain, so your emails don't land in spam.
Odoo traps on any server, OVHcloud included
In odoo.conf:
proxy_mode = True
list_db = False
admin_passwd = <a long random password>
workers = 5
gevent_port = 8072
workers must be above 0 in production, sized to your memory. Only then does Odoo start the separate process on port 8072 that serves /websocket.
In nginx, send /websocket to port 8072, and pass the real host and scheme:
location /websocket {
proxy_pass http://127.0.0.1:8072;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
Without proxy_mode and those headers, links in emails come out as http://.
Before you go live
- Odoo runs with
workersabove 0, sized to the server's memory /websocketgoes to port 8072, and chat worksproxy_mode = True, and links in emails start withhttps://list_db = Falseand a strongadmin_passwd- Only ports 80, 443 and your own SSH are open: security group or Edge Network Firewall, plus ufw
- Port 8069 times out from outside
- On managed PostgreSQL, only your Odoo server's IP is in the IPs list
- The SSL certificate renews on its own
- The backup timer ran, and the files are in the bucket
- The backup user gets "AccessDenied" when it tries to delete
- Logs rotate, and you get an alert when the disk is filling up or the site is down
- Odoo sends email through port 587 or 465
- A backup of the database and the files has been restored on another server, and the attachments open
Keep OVHcloud, hand over the Odoo work
You can keep your OVHcloud server and still not run Odoo yourself. With bring your own server, the server stays in your OVHcloud account and your chosen region. We run Odoo on it: deploys from Git, staging, backups, updates and help from Odoo experts. You keep the server running and big enough. Our software connects out from your server, so you don't open any port to us.
Skysize is built by ex-Odoo engineers and is ISO 27001 certified.
Talk to an Odoo hosting expert
FAQ
Is OVHcloud good for Odoo? Yes. It's a European provider with many regions. Pick a server with enough memory. Choose a Public Cloud instance if you want a private network or managed PostgreSQL.
Can Odoo use OVHcloud managed PostgreSQL?
Yes. Create the odoo role as avnadmin, run GRANT odoo TO avnadmin;, then create the database with OWNER odoo. Allow your Odoo server's IP, and set db_sslmode. Back up the filestore separately.
Are OVHcloud snapshots enough for Odoo backups? Not on their own. Also save the database and the filestore together, off the server, and test a restore.
Why can't Odoo send email from my Public Cloud instance? Outgoing port 25 is blocked on Public Cloud instances by default. Use your email provider's SMTP server on port 587 or 465.
Why is my managed database unreachable even though it shows as ready? No IP is allowed by default. Add your Odoo server's IP in the Configuration tab, under IPs.
Can I limit an Object Storage user to one bucket? Yes, with a user policy in JSON. It doesn't work on the user who owns the bucket. Create a second user for the server.