|
| 1 | +--- |
| 2 | +SPDX-License-Identifier: MIT |
| 3 | +path: "/tutorials/how-to-transfer-database-to-hetzner-cloud" |
| 4 | +slug: "how-to-transfer-database-to-hetzner-cloud" |
| 5 | +date: "2025-05-20" |
| 6 | +title: "How to Transfer Your Database to Hetzner Cloud" |
| 7 | +short_description: "Easily migrate your database to Hetzner with DBConvert Streams. Follow this guide to transfer your data securely and efficiently while reducing hosting costs." |
| 8 | +tags: ["Database", "MySQL", "PostgreSQL", "Docker", "Migration"] |
| 9 | +author: "Dmitry Narizhnykh" |
| 10 | +author_link: "https://github.com/slotix" |
| 11 | +author_img: "https://avatars.githubusercontent.com/u/684169" |
| 12 | +author_description: "Founder and CEO of DBConvert. Building tools for effortless database migration and replication across different platforms." |
| 13 | +language: "en" |
| 14 | +available_languages: ["en"] |
| 15 | +header_img: "header-8" |
| 16 | +cta: "cloud" |
| 17 | +--- |
| 18 | + |
| 19 | +## Introduction |
| 20 | + |
| 21 | +If you're currently hosting your database on another cloud provider and watching your monthly bills grow, migrating to Hetzner can dramatically reduce your costs while potentially improving performance. Many users report equivalent or better performance compared to much more expensive cloud options, especially for database workloads. |
| 22 | + |
| 23 | +In this guide, I'll walk you through transferring your databases to Hetzner using [DBConvert Streams](https://docs.dbconvert.com/) — a powerful database migration platform designed to simplify moving data between different hosting environments and database types. |
| 24 | + |
| 25 | +**Prerequisites** |
| 26 | + |
| 27 | +Before you start, make sure you have: |
| 28 | + |
| 29 | +* An [account with DBConvert](https://streams.dbconvert.com/) (free trial for 14 days) |
| 30 | +* A Hetzner Cloud account |
| 31 | +* Your source database credentials (e.g., AWS RDS, Google Cloud SQL, Azure DB) |
| 32 | +* Basic familiarity with Linux commands |
| 33 | +* [SSH key](https://community.hetzner.com/tutorials/howto-ssh-key) added to your Hetzner Cloud account |
| 34 | + |
| 35 | +**Example terminology** |
| 36 | + |
| 37 | +* Server: `<10.0.0.1>` |
| 38 | + |
| 39 | +## Step 1 - Create a Hetzner Cloud Server |
| 40 | + |
| 41 | +First, let's create a Hetzner [cloud server with Docker pre-installed](https://console.hetzner.cloud/deploy/docker-ce). |
| 42 | + |
| 43 | +> For a step-by-step guide, see [this getting started](https://docs.hetzner.com/cloud/servers/getting-started/creating-a-server). |
| 44 | +
|
| 45 | +* For the image, choose the "Apps" tab and select "Docker CE" |
| 46 | +* Select a server type based on your database needs: |
| 47 | + * For development/small production databases: CPX21 (2 vCPU, 4GB RAM) |
| 48 | + * For medium workloads: CPX31 (4 vCPU, 8GB RAM) |
| 49 | + * For larger production databases: CPX41 (8 vCPU, 16GB RAM) |
| 50 | +* Add your SSH key or create a new one |
| 51 | + |
| 52 | +**Note:** The "CPX" line is recommended for database workloads as they offer NVMe SSD storage with higher I/O performance compared to the standard "CX" instances. |
| 53 | + |
| 54 | +## Step 2 - Set Up PostgreSQL on Hetzner |
| 55 | + |
| 56 | +For simplicity in this tutorial, we'll deploy both [DBConvert Streams](https://github.com/slotix/dbconvert-streams-public) and our target PostgreSQL database on the same server. |
| 57 | + |
| 58 | +1. SSH into your new Hetzner server: |
| 59 | + |
| 60 | + > Replace `<10.0.0.1>` with the IP address of your cloud server. |
| 61 | +
|
| 62 | + ```bash |
| 63 | + ssh root@<10.0.0.1> |
| 64 | + ``` |
| 65 | + |
| 66 | +2. Create directories for PostgreSQL data and configuration: |
| 67 | + |
| 68 | + ```bash |
| 69 | + mkdir -p ~/pg-data ~/pg-conf |
| 70 | + ``` |
| 71 | + |
| 72 | +3. Run PostgreSQL in Docker: |
| 73 | + |
| 74 | + ```bash |
| 75 | + docker run -d \ |
| 76 | + --name postgres \ |
| 77 | + -e POSTGRES_USER=pguser \ |
| 78 | + -e POSTGRES_PASSWORD=strongpassword \ |
| 79 | + -e POSTGRES_DB=mydb \ |
| 80 | + -v ~/pg-data:/var/lib/postgresql/data \ |
| 81 | + -p 5432:5432 \ |
| 82 | + postgres:16 |
| 83 | + ``` |
| 84 | + |
| 85 | + Make sure to replace `strongpassword` with a secure password and `mydb` with your desired database name. |
| 86 | + |
| 87 | +4. Verify PostgreSQL is running: |
| 88 | + |
| 89 | + ```bash |
| 90 | + docker ps |
| 91 | + ``` |
| 92 | + |
| 93 | +You should see your PostgreSQL container running on port 5432. |
| 94 | + |
| 95 | +## Step 3 - Deploy DBConvert Streams |
| 96 | + |
| 97 | +Now, let's install DBConvert Streams on the same server: |
| 98 | + |
| 99 | +1. Download and install DBConvert Streams: |
| 100 | + |
| 101 | + ```bash |
| 102 | + curl -fsSL https://dbconvert.nyc3.digitaloceanspaces.com/downloads/streams/latest/docker-install.sh | sh |
| 103 | + ``` |
| 104 | + |
| 105 | + <details> |
| 106 | + <summary>Click here to view the output</summary> |
| 107 | + |
| 108 | + ```shellsession |
| 109 | + root@example-server:~# curl -fsSL https://dbconvert.nyc3.digitaloceanspaces.com/downloads/streams/latest/docker-install.sh | sh |
| 110 | + Installing gum using binary installation... |
| 111 | + |
| 112 | + ┌──────────────────────────────────────────────────────────────┐ |
| 113 | + │ │ |
| 114 | + │ Welcome to DBConvert Streams Installation │ |
| 115 | + │ This wizard will guide you through the installation process. │ |
| 116 | + │ │ |
| 117 | + └──────────────────────────────────────────────────────────────┘ |
| 118 | + |
| 119 | + Checking latest available version... |
| 120 | + Latest version available: v1.3.0 |
| 121 | + |
| 122 | + ┌──────────────────────────────────────────┐ |
| 123 | + │ │ |
| 124 | + │ Installing DBConvert Streams │ |
| 125 | + │ Version: v1.3.0 │ |
| 126 | + │ Directory: /opt/dbconvert-streams-docker │ |
| 127 | + │ │ |
| 128 | + └──────────────────────────────────────────┘ |
| 129 | + |
| 130 | + Starting installation... |
| 131 | + Installing DBConvert Streams... |
| 132 | + Checking prerequisites... |
| 133 | + Downloading deployment files... |
| 134 | + Updating version information in .env file... |
| 135 | + |
| 136 | + [...] |
| 137 | + |
| 138 | + Self-signed certificate generated successfully! |
| 139 | + Private key: config/certs/private.key |
| 140 | + Certificate: config/certs/certificate.crt |
| 141 | + Setting up nginx configuration... |
| 142 | + Configuring nginx with HTTP only... |
| 143 | + Nginx configuration complete! |
| 144 | + Installation complete! |
| 145 | + DBConvert Streams Version Information |
| 146 | + |
| 147 | + Latest available version: |
| 148 | + Latest version: v1.3.0 |
| 149 | + |
| 150 | + Configured versions in .env file: |
| 151 | + streams: v1.3.0 |
| 152 | + stream-ui: v1.3.2 |
| 153 | + |
| 154 | + No DBConvert Streams services are currently running |
| 155 | + |
| 156 | + ┌──────────────────────────────────────────────────────────────────────────────────┐ |
| 157 | + │ │ |
| 158 | + │ Installation Complete! │ |
| 159 | + │ Your DBConvert Streams installation is ready. │ |
| 160 | + │ │ |
| 161 | + │ To start the services: │ |
| 162 | + │ 1. Change to the installation directory: │ |
| 163 | + │ cd /opt/dbconvert-streams-docker │ |
| 164 | + │ 2. Run the start script: │ |
| 165 | + │ For HTTP: ./start.sh │ |
| 166 | + │ For HTTPS: ./start.sh --secure │ |
| 167 | + │ │ |
| 168 | + │ You can switch between HTTP and HTTPS modes at any time using the --secure flag. │ |
| 169 | + │ │ |
| 170 | + └──────────────────────────────────────────────────────────────────────────────────┘ |
| 171 | + ``` |
| 172 | + |
| 173 | + </details> |
| 174 | + |
| 175 | +2. Navigate to the installation directory and start the services: |
| 176 | + |
| 177 | + ```bash |
| 178 | + cd /opt/dbconvert-streams-docker/ |
| 179 | + ./start.sh |
| 180 | + ``` |
| 181 | + |
| 182 | + After the services start up, you'll see a message with your service URLs: |
| 183 | + |
| 184 | + ``` |
| 185 | + ┌────────────────────────────────────────────────────────────────────┐ |
| 186 | + │ │ |
| 187 | + │ Service URLs │ |
| 188 | + │ │ |
| 189 | + │ • UI: http://<10.0.0.1> │ |
| 190 | + │ • API: http://<10.0.0.1>/api/ │ |
| 191 | + │ │ |
| 192 | + │ Setup complete! You can now access the services at the URLs above. │ |
| 193 | + │ │ |
| 194 | + └────────────────────────────────────────────────────────────────────┘ |
| 195 | + ``` |
| 196 | + |
| 197 | +## Step 4 - Obtain and Enter Your API Key |
| 198 | + |
| 199 | +1. Go to https://streams.dbconvert.com/account |
| 200 | +2. Sign in using your preferred authentication method |
| 201 | +3. Copy your API key from the account dashboard |
| 202 | +4. Return to your DBConvert Streams instance: |
| 203 | + ```http |
| 204 | + http://<10.0.0.1> |
| 205 | + ``` |
| 206 | +5. Paste the API key in the provided field |
| 207 | + |
| 208 | +DBConvert Streams offers a free trial that includes: |
| 209 | +* 5GB of data transfer |
| 210 | +* 14 days of unlimited access to all features |
| 211 | +* No credit card required to start |
| 212 | + |
| 213 | +## Step 5 - Connect Your Source Database |
| 214 | + |
| 215 | +### Step 5.1 - Configure Source Database Access |
| 216 | + |
| 217 | +Make sure DBConvert Streams can can access your source database. |
| 218 | + |
| 219 | +This step is different, depending on your provider. |
| 220 | + |
| 221 | +* Google Cloud SQL MySQL |
| 222 | + |
| 223 | + 1. In the Google Cloud Console, navigate to your Cloud SQL instance |
| 224 | + 2. Go to the "Connections" tab |
| 225 | + 3. Under "Networking," select "Add network" |
| 226 | + 4. Add your Hetzner server's IP address |
| 227 | + 5. Save your changes |
| 228 | + |
| 229 | +  |
| 230 | + |
| 231 | +### Step 5.2 - Create the Source Connection |
| 232 | + |
| 233 | +1. Return to your DBConvert Streams instance: |
| 234 | + ```http |
| 235 | + http://<10.0.0.1> |
| 236 | + ``` |
| 237 | +2. Click the quick action "Create Connection" in the dashboard |
| 238 | +3. Select your database type (e.g., MySQL) |
| 239 | +4. Enter your connection details: |
| 240 | + * Name: Optional. Give your connection a descriptive name |
| 241 | + * Server: Your source database IP/hostname |
| 242 | + * Port: Database port (e.g., 3306 for MySQL) |
| 243 | + * User ID: Your database username |
| 244 | + * Password: Your database password |
| 245 | + * Database: Source database name |
| 246 | +5. Click "Add" |
| 247 | +6. Test the connection |
| 248 | +7. Click "Update" to store the connection |
| 249 | + |
| 250 | + |
| 251 | + |
| 252 | +## Step 6 - Connect Your Hetzner PostgreSQL Database |
| 253 | + |
| 254 | +1. Return to the dashboard |
| 255 | +2. Click the quick action "Create Connection" in the dashboard |
| 256 | +3. Select "PostgreSQL" |
| 257 | +4. Enter the connection details: |
| 258 | + * Host: Your Hetzner server IP |
| 259 | + * Port: 5432 |
| 260 | + * Username: pguser |
| 261 | + * Password: Your PostgreSQL password |
| 262 | + * Database: mydb |
| 263 | +5. Click "Add" |
| 264 | +6. Test the connection |
| 265 | +7. Click "Update" to save the configuration |
| 266 | + |
| 267 | + |
| 268 | + |
| 269 | +## Step 7 - Configure Your Data Stream |
| 270 | + |
| 271 | +1. Return to the dashboard |
| 272 | +2. Click "Create New Stream" |
| 273 | +3. Select your source connection and click "Next" in the top right |
| 274 | +4. Choose tables to migrate |
| 275 | + |
| 276 | + Select transfer mode: |
| 277 | + * Convert/Migrate: For one-time migrations |
| 278 | + * CDC/Stream: For continuous replication |
| 279 | + |
| 280 | + Configure options: |
| 281 | + * Data bundle size |
| 282 | + * Index creation options |
| 283 | + * Custom SQL queries (if needed) |
| 284 | + |
| 285 | + When you're happy with your settings, click "Next" in the top right |
| 286 | + |
| 287 | +5. Select your target Hetzner PostgreSQL connection and click "Save" in the top right |
| 288 | + |
| 289 | + |
| 290 | + |
| 291 | +## Step 8 - Start and Monitor the Transfer |
| 292 | + |
| 293 | +1. Click the "Start" button to begin |
| 294 | +2. Monitor the transfer through the dashboard: |
| 295 | + * Track progress for each table |
| 296 | + * Monitor data transfer rates |
| 297 | + * View detailed logs |
| 298 | + * Pause or stop if needed |
| 299 | + |
| 300 | + |
| 301 | + |
| 302 | +## Step 9 - Verify Your Data |
| 303 | + |
| 304 | +1. Connect to your PostgreSQL database: |
| 305 | + |
| 306 | + ```bash |
| 307 | + docker exec -it postgres psql -U pguser -d mydb |
| 308 | + ``` |
| 309 | + |
| 310 | +2. Run verification queries: |
| 311 | + |
| 312 | + ```sql |
| 313 | + -- List all tables in the database |
| 314 | + \dt |
| 315 | + |
| 316 | + -- Get row counts for all tables |
| 317 | + SELECT |
| 318 | + schemaname as schema, |
| 319 | + relname as table_name, |
| 320 | + n_live_tup as row_count |
| 321 | + FROM pg_stat_user_tables |
| 322 | + ORDER BY n_live_tup DESC; |
| 323 | + |
| 324 | + -- Check table sizes including indexes |
| 325 | + SELECT |
| 326 | + table_schema, |
| 327 | + table_name, |
| 328 | + pg_size_pretty(pg_total_relation_size('"' || table_schema || '"."' || table_name || '"')) as total_size |
| 329 | + FROM information_schema.tables |
| 330 | + WHERE table_schema = 'public' |
| 331 | + ORDER BY pg_total_relation_size('"' || table_schema || '"."' || table_name || '"') DESC; |
| 332 | + ``` |
| 333 | + |
| 334 | +3. Compare these results with your source database to ensure: |
| 335 | + |
| 336 | + - All tables are present |
| 337 | + - Row counts match |
| 338 | + - Data sizes are reasonable |
| 339 | + - Primary keys and indexes are properly transferred |
| 340 | + |
| 341 | + If the numbers don't match or you notice any discrepancies, you may need to investigate specific tables in more detail. |
| 342 | + |
| 343 | +## Step 10 - Set Up Database Backups |
| 344 | + |
| 345 | +### Hetzner Snapshots |
| 346 | + |
| 347 | +1. In Hetzner Cloud Console, select your server |
| 348 | +2. Click "Snapshots" |
| 349 | +3. Click "Create Snapshot" |
| 350 | + |
| 351 | +See Snapshot prices at [hetzner.com/cloud](https://www.hetzner.com/cloud/#features). |
| 352 | + |
| 353 | +## Conclusion |
| 354 | + |
| 355 | +You've successfully migrated your database to Hetzner Cloud using DBConvert Streams. This setup provides you with: |
| 356 | + |
| 357 | +* Significant cost savings compared to other cloud providers |
| 358 | +* Full control over your database infrastructure |
| 359 | +* Automated backups for data security |
| 360 | +* High-performance NVMe storage |
| 361 | +* Simple management through Docker containers |
| 362 | + |
| 363 | +Remember to monitor your database performance and adjust server resources as needed. You can easily scale your Hetzner server up or down based on your workload requirements. |
| 364 | + |
| 365 | +##### License: MIT |
| 366 | + |
| 367 | +<!-- |
| 368 | +
|
| 369 | +Contributor's Certificate of Origin |
| 370 | +
|
| 371 | +By making a contribution to this project, I certify that: |
| 372 | +
|
| 373 | +(a) The contribution was created in whole or in part by me and I have |
| 374 | + the right to submit it under the license indicated in the file; or |
| 375 | +
|
| 376 | +(b) The contribution is based upon previous work that, to the best of my |
| 377 | + knowledge, is covered under an appropriate license and I have the |
| 378 | + right under that license to submit that work with modifications, |
| 379 | + whether created in whole or in part by me, under the same license |
| 380 | + (unless I am permitted to submit under a different license), as |
| 381 | + indicated in the file; or |
| 382 | +
|
| 383 | +(c) The contribution was provided directly to me by some other person |
| 384 | + who certified (a), (b) or (c) and I have not modified it. |
| 385 | +
|
| 386 | +(d) I understand and agree that this project and the contribution are |
| 387 | + public and that a record of the contribution (including all personal |
| 388 | + information I submit with it, including my sign-off) is maintained |
| 389 | + indefinitely and may be redistributed consistent with this project |
| 390 | + or the license(s) involved. |
| 391 | +
|
| 392 | +Signed-off-by: Dmitry Narizhnykh <streams@dbconvert.com> |
| 393 | +
|
| 394 | +--> |
0 commit comments