Databases

Provision managed databases in seconds. The platform supports MySQL, PostgreSQL, and Redis, all running as Docker containers on the same internal network as your applications.

Available Engines

๐Ÿฌ

MySQL

Version 8.0

Reliable relational database with full SQL support. Best for traditional web applications, CMS platforms, and e-commerce.

๐Ÿ˜

PostgreSQL

Version 16

Advanced relational database with JSONB, full-text search, and extensions. Best for complex applications and analytics.

โšก

Redis

Version 7

In-memory key-value store. Best for caching, session storage, queues, rate limiting, and real-time features.

Creating a Database

Via the Dashboard

  1. Navigate to Databases in the sidebar
  2. Click "Create Database"
  3. Choose an engine (MySQL, PostgreSQL, or Redis)
  4. Enter a name for the database (used as the container name)
  5. Optionally set a custom root/admin password (one will be generated if not provided)
  6. Click "Create"

The database will be provisioned in a few seconds. Connection details will be shown immediately.

Connection Strings

Each database gets a connection string that you should store as an environment variable in your project. The format depends on the engine:

MySQL

mysql://root:<password>@<container-name>:3306/<database-name>

# Example:
mysql://root:s3cretPass@myapp-mysql:3306/myapp

PostgreSQL

postgresql://postgres:<password>@<container-name>:5432/<database-name>

# Example:
postgresql://postgres:s3cretPass@myapp-postgres:5432/myapp

Redis

redis://default:<password>@<container-name>:6379

# Example (no auth):
redis://myapp-redis:6379

# Example (with password):
redis://default:s3cretPass@myapp-redis:6379

Tip: Store the connection string as an environment variable (e.g., DATABASE_URL) in your project settings. The platform encrypts it at rest and injects it at deploy time.

Code Examples

Node.js

MySQL (mysql2)

javascript
import mysql from 'mysql2/promise';

const connection = await mysql.createConnection(
  process.env.DATABASE_URL
);

const [rows] = await connection.execute('SELECT * FROM users');
console.log(rows);

PostgreSQL (pg)

javascript
import pg from 'pg';

const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
});

const result = await pool.query('SELECT * FROM users');
console.log(result.rows);

PostgreSQL (Prisma)

javascript
// prisma/schema.prisma
// datasource db {
//   provider = "postgresql"
//   url      = env("DATABASE_URL")
// }

import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

const users = await prisma.user.findMany();

Redis (ioredis)

javascript
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL);

await redis.set('key', 'value');
const value = await redis.get('key');
console.log(value); // "value"

Python

MySQL (pymysql)

python
import pymysql
import os

connection = pymysql.connect(
    host=os.environ.get("DB_HOST", "myapp-mysql"),
    port=3306,
    user="root",
    password=os.environ["DB_PASSWORD"],
    database="myapp",
)

with connection.cursor() as cursor:
    cursor.execute("SELECT * FROM users")
    rows = cursor.fetchall()
    print(rows)

PostgreSQL (psycopg2)

python
import psycopg2
import os

conn = psycopg2.connect(os.environ["DATABASE_URL"])
cur = conn.cursor()
cur.execute("SELECT * FROM users")
rows = cur.fetchall()
print(rows)

PostgreSQL (SQLAlchemy)

python
from sqlalchemy import create_engine
import os

engine = create_engine(os.environ["DATABASE_URL"])

with engine.connect() as conn:
    result = conn.execute("SELECT * FROM users")
    for row in result:
        print(row)

Redis

python
import redis
import os

r = redis.from_url(os.environ["REDIS_URL"])

r.set("key", "value")
value = r.get("key")
print(value)  # b"value"

PHP

MySQL (PDO)

php
<?php
$dsn = 'mysql:host=myapp-mysql;port=3306;dbname=myapp';
$pdo = new PDO($dsn, 'root', getenv('DB_PASSWORD'));

$stmt = $pdo->query('SELECT * FROM users');
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($rows);

PostgreSQL (PDO)

php
<?php
$dsn = 'pgsql:host=myapp-postgres;port=5432;dbname=myapp';
$pdo = new PDO($dsn, 'postgres', getenv('DB_PASSWORD'));

$stmt = $pdo->query('SELECT * FROM users');
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($rows);

Redis (phpredis)

php
<?php
$redis = new Redis();
$redis->connect('myapp-redis', 6379);

$redis->set('key', 'value');
$value = $redis->get('key');
echo $value; // "value"

Go

MySQL (database/sql)

go
package main

import (
    "database/sql"
    "fmt"
    "os"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, err := sql.Open("mysql", os.Getenv("DATABASE_URL"))
    if err != nil {
        panic(err)
    }
    defer db.Close()

    rows, err := db.Query("SELECT id, name FROM users")
    if err != nil {
        panic(err)
    }
    defer rows.Close()

    for rows.Next() {
        var id int
        var name string
        rows.Scan(&id, &name)
        fmt.Printf("User: %d - %s\n", id, name)
    }
}

PostgreSQL (pgx)

go
package main

import (
    "context"
    "fmt"
    "os"

    "github.com/jackc/pgx/v5"
)

func main() {
    ctx := context.Background()
    conn, err := pgx.Connect(ctx, os.Getenv("DATABASE_URL"))
    if err != nil {
        panic(err)
    }
    defer conn.Close(ctx)

    rows, _ := conn.Query(ctx, "SELECT id, name FROM users")
    for rows.Next() {
        var id int
        var name string
        rows.Scan(&id, &name)
        fmt.Printf("User: %d - %s\n", id, name)
    }
}

Redis (go-redis)

go
package main

import (
    "context"
    "fmt"
    "os"

    "github.com/redis/go-redis/v9"
)

func main() {
    ctx := context.Background()
    opt, _ := redis.ParseURL(os.Getenv("REDIS_URL"))
    rdb := redis.NewClient(opt)

    rdb.Set(ctx, "key", "value", 0)
    val, _ := rdb.Get(ctx, "key").Result()
    fmt.Println(val) // "value"
}

Ruby

MySQL (mysql2)

ruby
require 'mysql2'

client = Mysql2::Client.new(
  host: 'myapp-mysql',
  username: 'root',
  password: ENV['DB_PASSWORD'],
  database: 'myapp'
)

results = client.query('SELECT * FROM users')
results.each do |row|
  puts row
end

PostgreSQL (pg)

ruby
require 'pg'

conn = PG.connect(ENV['DATABASE_URL'])
result = conn.exec('SELECT * FROM users')
result.each do |row|
  puts row
end

Redis

ruby
require 'redis'

redis = Redis.new(url: ENV['REDIS_URL'])

redis.set('key', 'value')
value = redis.get('key')
puts value  # "value"

Connecting from Your App

Internal Network

All containers (apps and databases) run on the same Docker network. Your app connects to the database using the container name as the hostname. No ports need to be exposed externally.

Internal connection (recommended)

# From inside your app container:
DATABASE_URL=postgresql://postgres:pass@myapp-postgres:5432/myapp

# The hostname "myapp-postgres" resolves to the database container's IP
# on the internal Docker network. This is fast and secure.

External Access

By default, database ports are not exposed to the internet. If you need to connect from outside (e.g., a local development machine), use SSH tunneling (see below) or enable external access in the database settings.

Security warning: Exposing database ports to the internet is not recommended for production databases. Use SSH tunneling or VPN instead.

SSH Tunneling for Local Development

Use an SSH tunnel to securely connect to your database from your local machine:

SSH tunnel example (PostgreSQL)

bash
# Forward local port 5433 to the remote PostgreSQL container
ssh -L 5433:myapp-postgres:5432 user@system.rouic.com

# Then connect locally:
psql "postgresql://postgres:pass@localhost:5433/myapp"

SSH tunnel example (MySQL)

bash
# Forward local port 3307 to the remote MySQL container
ssh -L 3307:myapp-mysql:3306 user@system.rouic.com

# Then connect locally:
mysql -h 127.0.0.1 -P 3307 -u root -p myapp

Backups and Restores

Creating backups

Navigate to the database detail page in the dashboard and click "Create Backup". Backups are stored as compressed SQL dumps (MySQL/PostgreSQL) or RDB snapshots (Redis).

Scheduled backups

Enable scheduled backups in the database settings. Choose a frequency (daily, weekly) and a retention period. Old backups are automatically deleted.

Restoring from backup

Select a backup from the list and click "Restore". This will stop the database, replace the data volume with the backup, and restart it. All current data will be overwritten.

Warning: Restoring a backup is a destructive operation. Make sure you have a recent backup of the current data before restoring.

Query Browser

The built-in query browser lets you run SQL queries directly from the dashboard. Navigate to a database and click the "Query" tab.

  • Run SELECT, INSERT, UPDATE, DELETE, and DDL statements
  • Results are displayed in a paginated table
  • Query history is saved per database
  • For Redis, you can run any Redis command (GET, SET, KEYS, etc.)

Note: The query browser is intended for debugging and administrative tasks. For application queries, always connect from your app using the connection string.

Best Practices

Use connection pooling

Limit the number of database connections your application opens. Use a connection pool (e.g., PgBouncer for PostgreSQL, built-in pooling in mysql2/pg libraries).

Store connection strings as env vars

Never hardcode database credentials in your source code. Use environment variables that are encrypted by the platform.

Run migrations on deploy

Add a migration step to your Dockerfile or deploy script. Prisma: prisma migrate deploy. Django: python manage.py migrate.

Enable backups before going live

Always enable scheduled backups for production databases. Test restoring from a backup at least once.

Use Redis for caching, not primary storage

Redis data can be lost on restart (unless persistence is configured). Use it for caching and ephemeral data.

Monitor connection counts

Check the metrics dashboard for active connection counts. Running out of connections is a common issue in production.