├── .gitignore ├── tests ├── inventory └── test.yml ├── handlers └── main.yml ├── templates ├── user-override.conf ├── user-conf-d-override.conf ├── solana-rpc.env ├── solana-rpc.service ├── bigtable.json └── solana-rpc.sh ├── tasks ├── bigtable.yml ├── dependencies.yml ├── user.yml ├── rust.yml ├── optimisations.yml ├── main.yml ├── identity.yml ├── install.yml └── service.yml ├── meta └── main.yml ├── vars ├── testnet-default.yml ├── devnet-default.yml ├── mainnet-default.yml └── main.yml ├── LICENSE ├── defaults └── main.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | -------------------------------------------------------------------------------- /tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for solana-rpc-ansible 3 | -------------------------------------------------------------------------------- /templates/user-override.conf: -------------------------------------------------------------------------------- 1 | [Service] 2 | LimitNOFILE=700000 3 | LimitNPROC=700000 4 | -------------------------------------------------------------------------------- /templates/user-conf-d-override.conf: -------------------------------------------------------------------------------- 1 | [Manager] 2 | DefaultLimitNOFILE=700000 3 | DefaultLimitNPROC=700000 4 | -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - solana-rpc-ansible 6 | -------------------------------------------------------------------------------- /tasks/bigtable.yml: -------------------------------------------------------------------------------- 1 | # Set up bigtable 2 | - name: ensure bigtable json is present 3 | template: 4 | src: bigtable.json 5 | dest: "/home/solana/bigtable.json" 6 | owner: solana 7 | group: solana 8 | mode: 0600 9 | when: solana_bigtable_enabled 10 | -------------------------------------------------------------------------------- /tasks/dependencies.yml: -------------------------------------------------------------------------------- 1 | - name: install dependencies 2 | apt: 3 | pkg: ['libssl-dev', 'libudev-dev', 'pkg-config', 'zlib1g-dev', 'llvm', 'clang', 'make', 'git', 'acl'] 4 | state: present 5 | 6 | - name: install linux kernel tools 7 | apt: 8 | pkg: ["linux-tools-generic", "linux-tools-{{ ansible_kernel }}", "inotify-tools", "htop", "iotop", "powertop", "cpufrequtils"] 9 | state: present 10 | -------------------------------------------------------------------------------- /templates/solana-rpc.env: -------------------------------------------------------------------------------- 1 | SOLANA_METRICS_CONFIG={{ solana_metrics_config }} 2 | {% if solana_bigtable_enabled %} 3 | GOOGLE_APPLICATION_CREDENTIALS=/home/solana/bigtable.json 4 | {% endif %} 5 | {% if solana_banking_threads is defined and solana_banking_threads > 0 %} 6 | SOLANA_BANKING_THREADS={{ solana_banking_threads }} 7 | {% endif %} 8 | {% for env_v in solana_environment %} 9 | {{ env_v }} 10 | {% endfor %} 11 | -------------------------------------------------------------------------------- /tasks/user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Creating a solana user 4 | - name: create solana user 5 | user: 6 | name: solana 7 | password: '!' 8 | shell: /bin/bash 9 | 10 | # Get solana uid/gid 11 | - name: register uid of solana user 12 | command: id -u solana 13 | register: solana_uid 14 | changed_when: false 15 | 16 | # Get solana uid/gid 17 | - name: register uid of solana user 18 | command: id -g solana 19 | register: solana_gid 20 | changed_when: false 21 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: Linus Kendall 3 | role_name: solana_rpc 4 | description: Ansible role for deploying Solana RPC nodes 5 | company: Triton One 6 | license: MIT 7 | min_ansible_version: 2.8 8 | platforms: 9 | - name: Ubuntu 10 | versions: 11 | - bionic 12 | - xenial 13 | - name: Debian 14 | versions: 15 | - stretch 16 | - buster 17 | galaxy_tags: 18 | - solana 19 | 20 | dependencies: [] 21 | -------------------------------------------------------------------------------- /templates/solana-rpc.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Solana RPC Node 3 | After=network.target 4 | StartLimitIntervalSec=0 5 | 6 | [Service] 7 | Type=simple 8 | Restart=always 9 | RestartSec=1 10 | 11 | EnvironmentFile=/home/solana/rpc.env 12 | 13 | ExecStart=/home/solana/bin/solana-rpc.sh 14 | 15 | LimitNOFILE={{ solana_limit_nofile|default(700000) }} 16 | LimitNPROC={{ solana_limit_nproc|default(700000) }} 17 | 18 | WorkingDirectory=/home/solana/ 19 | 20 | [Install] 21 | WantedBy=default.target 22 | -------------------------------------------------------------------------------- /templates/bigtable.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "service_account", 3 | "project_id": "{{ solana_bigtable_project_id }}", 4 | "private_key_id": "{{ solana_bigtable_private_key_id }}", 5 | "private_key": "{{ solana_bigtable_private_key }}", 6 | "client_email": "{{ solana_bigtable_client_email }}", 7 | "client_id": "{{ solana_bigtable_client_id }}", 8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 9 | "token_uri": "https://oauth2.googleapis.com/token", 10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 11 | "client_x509_cert_url": "{{ solana_bigtable_client_x509_cert_url }}" 12 | } 13 | -------------------------------------------------------------------------------- /vars/testnet-default.yml: -------------------------------------------------------------------------------- 1 | solana_genesis_hash: 4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY 2 | 3 | 4 | solana_entrypoints: 5 | - entrypoint.testnet.solana.com:8001 6 | - entrypoint2.testnet.solana.com:8001 7 | - entrypoint3.testnet.solana.com:8001 8 | 9 | solana_metrics_config: host=https://metrics.solana.com:8086,db=tds,u=testnet_write,p=c4fa841aa918bf8274e3e2a44d77568d9861b3ea 10 | 11 | 12 | solana_known_validators: 13 | - 5D1fNXzvv5NjV1ysLjirC4WY92RNsVH18vjmcszZd8on 14 | - 7XSY3MrYnK8vq693Rju17bbPkCN3Z7KvvfvJx4kdrsSY 15 | - Ft5fbkqNa76vnsjYNwjDZUXoTWpP7VYm3mtsaQckQADN 16 | - 9QxCLckBiJc783jnMvXZubK4wH86Eqqvashtrwvcsgkv 17 | -------------------------------------------------------------------------------- /vars/devnet-default.yml: -------------------------------------------------------------------------------- 1 | solana_genesis_hash: EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG 2 | solana_entrypoints: 3 | - entrypoint.devnet.solana.com:8001 4 | - entrypoint2.devnet.solana.com:8001 5 | - entrypoint3.devnet.solana.com:8001 6 | - entrypoint4.devnet.solana.com:8001 7 | - entrypoint5.devnet.solana.com:8001 8 | 9 | solana_metrics_config: host=https://metrics.solana.com:8086,db=devnet,u=scratch_writer,p=topsecret 10 | 11 | 12 | solana_bpf_jit: true 13 | 14 | solana_known_validators: 15 | - dv1ZAGvdsz5hHLwWXsVnM94hWf1pjbKVau1QVkaMJ92 16 | - dv2eQHeP4RFrJZ6UeiZWoc3XTtmtZCUKxxCApCDcRNV 17 | - dv4ACNkpYPcE3aKmYDqZm9G5EB3J4MRoeE7WNDRBVJB 18 | - dv3qDFk1DTF36Z62bNvrCXe9sKATA6xvVy6A798xxAS 19 | -------------------------------------------------------------------------------- /vars/mainnet-default.yml: -------------------------------------------------------------------------------- 1 | solana_genesis_hash: 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d 2 | 3 | solana_entrypoints: 4 | - entrypoint.mainnet-beta.solana.com:8001 5 | - entrypoint2.mainnet-beta.solana.com:8001 6 | - entrypoint3.mainnet-beta.solana.com:8001 7 | - entrypoint4.mainnet-beta.solana.com:8001 8 | - entrypoint5.mainnet-beta.solana.com:8001 9 | 10 | solana_metrics_config: host=https://metrics.solana.com:8086,db=mainnet-beta,u=mainnet-beta_write,p=password 11 | 12 | solana_index_exclude_keys: 13 | - "kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6" 14 | - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" 15 | 16 | solana_known_validators: 17 | - 7Np41oeYqPefeNQEHSv1UDhYrehxin3NStELsSKCT4K2 18 | - GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ 19 | - DE1bawNcRJB9rVm3buyMVfr8mBEoyyu73NBovf2oXJsJ 20 | - CakcnaRDHka2gXyfbEd2d3xsvkJkqsLw2akB3zsN1D2S 21 | -------------------------------------------------------------------------------- /tasks/rust.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: download rust installer 3 | get_url: 4 | url: "https://sh.rustup.rs" 5 | dest: /usr/local/sbin/rustup-install.sh 6 | mode: 0755 7 | owner: root 8 | group: root 9 | checksum: "{{ rustup_checksum }}" 10 | 11 | - name: install rust 12 | command: "/usr/local/sbin/rustup-install.sh -y" 13 | args: 14 | chdir: /home/solana/ 15 | become: true 16 | become_user: solana 17 | register: install_output 18 | changed_when: false 19 | 20 | - name: debug 21 | debug: var=install_output 22 | 23 | - name: install rustfmt 24 | command: rustup component add rustfmt 25 | args: 26 | chdir: /home/solana/ 27 | environment: 28 | PATH: /bin:/usr/bin:/usr/local/bin:/home/solana/.cargo/bin 29 | become: true 30 | become_user: solana 31 | register: rustup_output 32 | changed_when: false 33 | 34 | - name: debug 35 | debug: var=rustup_output 36 | -------------------------------------------------------------------------------- /tasks/optimisations.yml: -------------------------------------------------------------------------------- 1 | # CPU governor settings, can be disabled with cpu_governor = bios 2 | - name: disable ondemand governor (get performance governor) 3 | systemd: 4 | name: ondemand 5 | enabled: false 6 | state: stopped 7 | 8 | - name: ensure cpu governor is set 9 | copy: 10 | content: "GOVERNOR=\"{{ cpu_governor }}\"" 11 | dest: /etc/default/cpufrequtils 12 | owner: root 13 | group: root 14 | mode: 0644 15 | when: cpu_governor != "bios" 16 | 17 | - name: ensure cpu governor is enabled 18 | command: cpupower frequency-set -g {{ cpu_governor }} 19 | become: true 20 | become_user: root 21 | when: cpu_governor != "bios" 22 | 23 | # Sysctl settings 24 | - name: ensure sysctl is set to sufficiently high value 25 | sysctl: 26 | name: "{{ item.key }}" 27 | value: "{{ item.value }}" 28 | sysctl_set: true 29 | state: present 30 | reload: true 31 | loop: "{{ sysctl_optimisations|dict2items }}" 32 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Gather variables on basis of solana network 3 | include_vars: "{{ item }}" 4 | with_first_found: 5 | - "{{ solana_network | lower }}.yml" 6 | - "{{ solana_network | lower }}-default.yml" 7 | 8 | # Install dependencies 9 | - include: dependencies.yml 10 | tags: 11 | - solana_install 12 | 13 | 14 | # Set up a solana user 15 | - include: user.yml 16 | tags: 17 | - solana_configure 18 | - solana_install 19 | - solana_run 20 | 21 | # Install rust 22 | - include: rust.yml 23 | when: install_rust is defined and install_rust 24 | tags: 25 | - solana_install 26 | 27 | # Install solana 28 | - include: install.yml 29 | tags: 30 | - solana_install 31 | 32 | 33 | # Set cpu optimisations 34 | - include: optimisations.yml 35 | tags: 36 | - solana_configure 37 | 38 | # Setup RPC identity 39 | - include: identity.yml 40 | tags: 41 | - solana_configure 42 | 43 | # Setup bigtable 44 | - include: bigtable.yml 45 | tags: 46 | - solana_configure 47 | 48 | 49 | # Setup solana service files 50 | - include: service.yml 51 | tags: 52 | - solana_run 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 rpcpool 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tasks/identity.yml: -------------------------------------------------------------------------------- 1 | # Configure solana key 2 | - name: ensure solana key is present 3 | command: /home/solana/.local/share/solana/install/active_release/bin/solana-keygen new -o /home/solana/identity.json --no-bip39-passphrase -s 4 | become: true 5 | become_user: solana 6 | args: 7 | chdir: /home/solana/ 8 | creates: /home/solana/identity.json 9 | when: (solana_keypairs is not defined or solana_keypairs|length == 0) and solana_generate_keypair 10 | 11 | - name: ensure solana keys are present 12 | copy: 13 | content: "{{ item.key }}" 14 | dest: "/home/solana/{{ item.name }}.json" 15 | owner: solana 16 | group: solana 17 | mode: 0600 18 | with_items: "{{ solana_keypairs }}" 19 | when: solana_keypairs is defined 20 | 21 | - name: set keypair to default 22 | command: /home/solana/.local/share/solana/install/active_release/bin/solana config set --keypair "{{ solana_public_key }}" 23 | become: true 24 | become_user: solana 25 | args: 26 | chdir: /home/solana/ 27 | changed_when: false 28 | 29 | - name: check solana pubkey 30 | command: /home/solana/.local/share/solana/install/active_release/bin/solana-keygen pubkey "{{ solana_public_key }}" 31 | register: res_pubkey 32 | changed_when: false 33 | 34 | - name: save public key hash 35 | set_fact: 36 | solana_public_key_hash: "{{ res_pubkey.stdout }}" 37 | -------------------------------------------------------------------------------- /tasks/install.yml: -------------------------------------------------------------------------------- 1 | - name: download solana-installer 2 | get_url: 3 | url: "https://raw.githubusercontent.com/solana-labs/solana/{{ solana_installer_version }}/install/solana-install-init.sh" 4 | dest: /usr/local/sbin/solana-install-init.sh 5 | mode: 0755 6 | owner: root 7 | group: root 8 | checksum: "{{ solana_checksums[solana_installer_version] }}" 9 | 10 | - name: install solana 11 | command: "/usr/local/sbin/solana-install-init.sh {{ solana_version }}" 12 | args: 13 | chdir: /home/solana/ 14 | become: true 15 | become_user: solana 16 | register: install_output 17 | changed_when: false 18 | 19 | - name: debug 20 | debug: var=install_output 21 | 22 | - name: ensure solana storage directories are avaiable 23 | file: 24 | path: "{{ item }}" 25 | owner: solana 26 | group: solana 27 | mode: 0755 28 | state: directory 29 | with_items: 30 | - "/home/solana/bin" 31 | - "/home/solana/src" 32 | - "{{ solana_root }}" 33 | - "{{ solana_ledger_location }}" 34 | - "{{ solana_ledger_location }}/rocksdb" 35 | - "{{ solana_accounts_location }}" 36 | 37 | - name: clone solana src 38 | git: 39 | repo: https://github.com/solana-labs/solana.git 40 | dest: /home/solana/src/solana 41 | clone: true 42 | version: "{{ solana_source_version }}" 43 | become: true 44 | become_user: solana 45 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Performance optimisations 3 | 4 | cpu_governor: performance 5 | 6 | sysctl_optimisations: {} 7 | 8 | # Install rust? 9 | install_rust: true 10 | 11 | # Solana variables 12 | 13 | solana_installer_version: v1.10.38 14 | 15 | solana_version: stable 16 | 17 | solana_source_version: v1.10.38 18 | 19 | solana_root: /solana 20 | 21 | solana_ledger_location: /solana/ledger 22 | 23 | solana_accounts_location: /solana/ledger/accounts 24 | 25 | solana_authorized_voters: [] 26 | 27 | solana_frozen_accounts: [] 28 | 29 | solana_full_rpc_api: true 30 | 31 | solana_rpc_history: true 32 | 33 | solana_realtime_scheduler: false 34 | 35 | solana_index_exclude_keys: [] 36 | 37 | solana_generate_keypair: true 38 | 39 | solana_public_key: /home/solana/identity.json 40 | 41 | solana_keypairs: [] 42 | 43 | solana_environment: 44 | - "RUST_LOG=solana=info,solana_streamer::streamer=warn" 45 | 46 | solana_gossip_port: '8001' 47 | 48 | solana_rpc_port: '8899' 49 | 50 | solana_rpc_bind_address: '127.0.0.1' 51 | 52 | solana_dynamic_port_range: '8002-8020' 53 | 54 | solana_wal_recovery_mode: skip_any_corrupted_record 55 | 56 | solana_limit_ledger_size: '' 57 | 58 | solana_trusted_validators: [] 59 | 60 | solana_account_index: program-id spl-token-owner spl-token-mint 61 | 62 | solana_bigtable_enabled: false 63 | 64 | solana_network: mainnet 65 | 66 | solana_enabled_services: 67 | - solana-rpc 68 | 69 | solana_disabled_services: [] 70 | 71 | # solana_metrics_config 72 | # solana_pubsub_max_connections 73 | # solana_bpf_jit 74 | # solana_accounts_db_caching 75 | # solana_snapshot_interval_slots 76 | # solana_wait_for_supermajority 77 | # solana_hard_fork 78 | # solana_accounts_shrink_path 79 | # solana_rpc_faucet_address: 80 | # solana_snapshot_compression: 'none' 81 | # solana_banking_threads: 82 | # solana_rpc_threads 83 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Checksums for the solana installer file 4 | solana_checksums: 5 | 'v1.2.23': sha256:acf36fb05dda07e15ebd85fb44f63c2fbfd80ec3a12a888428a7d1a7ae7b8b99 6 | 'v1.3.21': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 7 | 'v1.4.9': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 8 | 'v1.4.24': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 9 | 'v1.4.25': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 10 | 'v1.4.26': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 11 | 'v1.5.4': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 12 | 'v1.5.8': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 13 | 'v1.5.11': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 14 | 'v1.5.12': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 15 | 'v1.5.14': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 16 | 'v1.5.16': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 17 | 'v1.6.8': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 18 | 'v1.6.9': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 19 | 'v1.6.10': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 20 | 'v1.8.16': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 21 | 'v1.9.9': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 22 | 'v1.10.0': sha256:dff125af04d93dc24e270645490fa414fe524be4a5136f8a16554c587033f9e3 23 | 'v1.10.38': sha256:dff125af04d93dc24e270645490fa414fe524be4a5136f8a16554c587033f9e3 24 | 'stable': sha256:14635ca16308f8067e204ef29815bfaa4d692b96a7a7a165aa126b2663452efd 25 | 26 | # Checksum for rust up 27 | rustup_checksum: sha256:173f4881e2de99ba9ad1acb59e65be01b2a44979d83b6ec648d0d22f8654cbce 28 | -------------------------------------------------------------------------------- /tasks/service.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: ensure xdg runtime dir is set in bashrc 4 | lineinfile: 5 | path: "/home/solana/.bashrc" 6 | regexp: "^export XDG_RUNTIME_DIR" 7 | line: "export XDG_RUNTIME_DIR=/run/user/$(id -u)" 8 | 9 | - name: ensure solana user can use lingering systemd 10 | command: loginctl enable-linger solana 11 | changed_when: false 12 | 13 | - name: ensure solana user is part of systemd-journal group 14 | user: 15 | name: solana 16 | groups: systemd-journal 17 | append: true 18 | 19 | - name: ensure solana directories exists 20 | file: 21 | path: "{{ item }}" 22 | state: directory 23 | mode: 0755 24 | owner: root 25 | group: root 26 | with_items: 27 | - "/etc/systemd/system/user@.service.d/" 28 | - "/etc/systemd/user.conf.d/" 29 | 30 | - name: install user service override 31 | template: 32 | src: user-override.conf 33 | dest: /etc/systemd/system/user@.service.d/override.conf 34 | owner: root 35 | group: root 36 | mode: 0644 37 | 38 | - name: install user override values 39 | template: 40 | src: user-conf-d-override.conf 41 | dest: /etc/systemd/user.conf.d/override.conf 42 | owner: root 43 | group: root 44 | mode: 0644 45 | 46 | - name: ensure systemd user dir is available 47 | file: 48 | path: "/home/solana/.config/systemd/user/" 49 | owner: solana 50 | group: solana 51 | mode: 0750 52 | state: directory 53 | 54 | - name: copy rpc.sh 55 | template: 56 | src: solana-rpc.sh 57 | dest: /home/solana/bin/solana-rpc.sh 58 | owner: solana 59 | group: solana 60 | mode: 0755 61 | 62 | - name: copy rpc.env 63 | template: 64 | src: solana-rpc.env 65 | dest: /home/solana/rpc.env 66 | owner: solana 67 | group: solana 68 | mode: 0640 69 | 70 | - name: "install {{ item }} service" 71 | template: 72 | src: "{{ item }}.service" 73 | dest: "/home/solana/.config/systemd/user/{{ item }}.service" 74 | owner: solana 75 | group: solana 76 | mode: 0644 77 | with_items: 78 | - solana-rpc 79 | 80 | - name: setup services 81 | systemd: 82 | daemon_reload: true 83 | name: "{{ item }}" 84 | enabled: true 85 | scope: user 86 | become: true 87 | become_user: solana 88 | environment: 89 | XDG_RUNTIME_DIR: "/run/user/{{ solana_uid.stdout }}" 90 | with_items: "{{ solana_enabled_services }}" 91 | 92 | - name: setup services (disabled) 93 | systemd: 94 | daemon_reload: true 95 | name: "{{ item }}" 96 | enabled: false 97 | scope: user 98 | become: true 99 | become_user: solana 100 | environment: 101 | XDG_RUNTIME_DIR: "/run/user/{{ solana_uid.stdout }}" 102 | with_items: "{{ solana_disabled_services }}" 103 | -------------------------------------------------------------------------------- /templates/solana-rpc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | # Remove empty snapshots 8 | find "{{ solana_ledger_location }}" -name 'snapshot-*' -size 0 -print -exec rm {} \; || true 9 | 10 | # Start solana rpc node, intended to be used with journalctl/systemctl. Logs will go to journalctl. 11 | exec /home/solana/.local/share/solana/install/active_release/bin/solana-validator \ 12 | --identity {{ solana_public_key }} \ 13 | {% if solana_gossip_host is defined and solana_gossip_host|length > 0 %} 14 | --gossip-host {{ solana_gossip_host }} 15 | {% else %} 16 | {% for entrypoint in solana_entrypoints %} 17 | --entrypoint {{ entrypoint }} \ 18 | {% endfor %} 19 | {% endif %} 20 | --ledger {{ solana_ledger_location }} \ 21 | {% if solana_accounts_location is defined %} 22 | --accounts {{ solana_accounts_location }} \ 23 | {% endif %} 24 | {% if solana_snapshots_location is defined %} 25 | --snapshots {{ solana_snapshots_location }} \ 26 | {% endif %} 27 | {% if solana_snapshot_compression is defined %} 28 | --snapshot-compression {{ solana_snapshot_compression }} \ 29 | {% endif %} 30 | --log - \ 31 | --gossip-port {{ solana_gossip_port }} \ 32 | --rpc-port {{ solana_rpc_port }} \ 33 | --rpc-bind-address {{ solana_rpc_bind_address }} \ 34 | --dynamic-port-range {{ solana_dynamic_port_range }} \ 35 | --wal-recovery-mode {{ solana_wal_recovery_mode }} \ 36 | --limit-ledger-size {{ solana_limit_ledger_size }} \ 37 | --private-rpc \ 38 | --no-snapshot-fetch \ 39 | --no-genesis-fetch \ 40 | --no-port-check \ 41 | --no-voting \ 42 | {% if solana_full_rpc_api %} 43 | {% if (solana_version == "stable" or (solana_version is version('1.9', '>=') and solana_version is version('1.9.6', '>=')) or (solana_version is version('1.9', '<') and solana_version is version('1.8.15', '>='))) %} 44 | --full-rpc-api \ 45 | {% endif %} 46 | {% endif %} 47 | {% if solana_trusted_validators|length > 0 %} 48 | --no-untrusted-rpc \ 49 | --halt-on-trusted-validators-accounts-hash-mismatch \ 50 | {% endif %} 51 | {% if solana_rpc_history %} 52 | --enable-cpi-and-log-storage \ 53 | --enable-rpc-transaction-history \ 54 | {% endif %} 55 | --account-index {{ solana_account_index }} \ 56 | --expected-genesis-hash {{ solana_genesis_hash }} \ 57 | {% if solana_rpc_threads is defined and solana_rpc_threads > 0 %} 58 | --rpc-threads {{ solana_rpc_threads }} \ 59 | {% endif %} 60 | {% if solana_bigtable_enabled is defined and solana_bigtable_enabled %} 61 | --enable-rpc-bigtable-ledger-storage \ 62 | {% endif %} 63 | {% if solana_bigtable_upload_enabled is defined and solana_bigtable_upload_enabled %} 64 | --enable-bigtable-ledger-upload \ 65 | {% endif %} 66 | {% if solana_rpc_faucet_address is defined and solana_rpc_faucet_address|length > 0 %} 67 | --rpc-faucet-address {{ solana_rpc_faucet_address }} \ 68 | {% endif %} 69 | {% if solana_pubsub_max_connections is defined %} 70 | --rpc-pubsub-max-connections {{ solana_pubsub_max_connections }} \ 71 | {% endif %} 72 | {% if solana_bpf_jit is defined and solana_bpf_jit %} 73 | --bpf-jit \ 74 | {% endif %} 75 | {% if solana_accounts_db_caching is defined and solana_accounts_db_caching %} 76 | --no-accounts-db-caching \ 77 | {% endif %} 78 | {% if solana_snapshot_interval_slots is defined %} 79 | --snapshot-interval-slots {{ solana_snapshot_interval_slots }} \ 80 | {% endif %} 81 | {% if solana_expected_shred_version is defined and solana_expected_shred_version|length > 0 %} 82 | --expected-shred-version {{ solana_expected_shred_version }} \ 83 | {% endif %} 84 | {% if solana_expected_bank_hash is defined and solana_expected_bank_hash|length > 0 %} 85 | --expected-bank-hash {{ solana_expected_bank_hash }} \ 86 | {% endif %} 87 | {% if solana_wait_for_supermajority is defined and solana_wait_for_supermajority|length > 0 %} 88 | --wait-for-supermajority {{ solana_wait_for_supermajority }} \ 89 | {% endif %} 90 | {% if solana_hard_fork is defined and solana_hard_fork|length > 0 %} 91 | --hard-fork {{ solana_hard_fork }} \ 92 | {% endif %} 93 | {% if solana_accounts_shrink_path is defined and solana_accounts_shrink_path|length > 0%} 94 | --accounts-shrink-path {{ solana_accounts_shrink_path }} \ 95 | {% endif %} 96 | {% for ac in solana_frozen_accounts %} 97 | --frozen-account {{ ac }} \ 98 | {% endfor %} 99 | {% for key in solana_index_exclude_keys %} 100 | --account-index-exclude-key {{ key }} \ 101 | {% endfor %} 102 | {% for voter in solana_authorized_voters %} 103 | --authorized-voter {{ voter }} \ 104 | {% endfor %} 105 | {% for validator in solana_known_validators %} 106 | --known-validator {{ validator }} {% if not loop.last %}\ 107 | {% endif %} 108 | {% endfor %} 109 | 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Solana RPC role 2 | ========= 3 | 4 | An Ansible role to deploy a Solana RPC node. This configures the validator software in RPC mode running under the user `solana`. The RPC service is installed as a user service running under this same user. 5 | 6 | Updates 7 | ------------ 8 | 9 | - 16/02 - From Solana 1.8.15 (mainnet) and 1.9.6 (testnet) onwards you will need to specify `solana_full_rpc_api: true` for this role to actually create a fully exposed RPC API node. 10 | 11 | Hardware Requirements 12 | ------------ 13 | 14 | An RPC server requires _at least_ the same specs as a Solana validator, but typically has higher requirements. In particular, we recommend using 256 GB of RAM in order to store indexes. For more information about hardware requirements, please see [https://docs.solana.com/running-validator/validator-reqs](https://docs.solana.com/running-validator/validator-reqs). We strongly recommend you use a baremetal provider (not Hetzner) rather than a cloud provider unless you know what you're doing (and then why are you reading this page?). 15 | 16 | Before deploy you should prepare the host so that the directory that you use for your Accounts database and your Ledger location are properly configured. This can include setting up a tmpfs folder for accounts and a separate filesystem (ideally on an NVME drive) for the ledger. A common way to configure this might be: 17 | 18 | ``` 19 | /solana/tmpfs - a 100 GB tmpfs partition to hold accounts state 20 | /solana/ledger - a 2 TB NVME drive to hold ledger 21 | ``` 22 | 23 | ### Why bare metal and not cloud? 24 | 25 | Cloud servers (AWS, GCP, etc.) are generally unsuitable for Solana for a number of reasons: 26 | 27 | 1) Egress is really expensive and Solana will use a lot of egress 28 | 2) The single core performance is generally too low and cannot boost in the way that baremetal can 29 | 3) Many cloud providers do not want the kind of workload that Solana is on their lower cost instances, leading you to use very expensive baremetal instances. 30 | 31 | ### Why not Hetzner? 32 | 33 | Hetzner has decided that they do not want Solana RPC services running on their network. They actively block connections to Solana entrypoints and ratelimit any Solana traffic. Not only will your Solana node struggle to keep up with the network (on mainnet it will likely never catch up), but Hetzner is also very likely to shut down your account. 34 | 35 | Software Requirements 36 | ------------ 37 | 38 | * Ansible >= 2.7 (tested primarily on Ansible 2.8) 39 | * Ubuntu 18.04+ on the target deployment machine 40 | 41 | This role assumes some familiarity with the Solana validator software deployment process. 42 | 43 | Role Variables 44 | -------------- 45 | 46 | The deploy ensures that the checksum for the version of solana-installer that you are downloading matches one given in `vars/main.yml`. In case you want to insatll a solana version not listed there, it is good if you first download and check the sha256 checksum of the solana-installer script (https://raw.githubusercontent.com/solana-labs/solana/master/install/solana-install-init.sh). 47 | 48 | There are a large number of configurable parameters for Solana. Many of these have workable defaults, and you can use this role to deploy a Solana RPC node without changing any of the default values and you should be able to have a decent experience. If you run this role without specifying any parameters, it'll configure a standard `mainnet` RPC node. 49 | 50 | ### Basic variables 51 | 52 | These are the basic variables that configure the setup of the validators. They have default values but you probably want to customise them based on your setup. 53 | 54 | | Name | Default value | Description | 55 | |----------------------|----------------------|----------------------------| 56 | | `solana_version` | stable | The solana version to install. | 57 | | `solana_full_rpc_api` | `true` | Whether to enable the full RPC API or not. That's typically what you want. | 58 | | `solana_root` | /solana | Main directory for solana ledger and accounts | 59 | | `solana_ledger_location` | /solana/ledger | Storage for solana ledger (should be on NVME) | 60 | | `solana_accounts_location` | /solana/ledger/accounts | Storage for solana accounts information. In case you use tmpfs for accounts this should be a subdirectory of your tmpfs mount point (e.g. `/solana/tmpfs/accounts` in case tmpfs is mounted on `/solana/tmpfs` | 61 | | `solana_snapshots_location` | | Storage for solana snapshots. Can be useful to keep on a separate NVME from your ledger. | 62 | | `solana_keypairs` | `[]` | List of keypairs to copy to the validator node. Each entry in the list should have a `key` and `name` entry. This will create `/home/solana/.json` containing the value of `key`. | 63 | | `solana_generate_keypair` | true | Whether or not to generate a keypair. If you haven't specified `solana_keypairs` and you set this to true, a new key will be generated and placed in /home/solana/identity.json | 64 | | `solana_public_key` | `/home/solana/identity.json` | Location of the identity of the validator node. | 65 | | `solana_network` | mainnet | The solana network that this node is supposed to be part of | 66 | | `solana_environment` | see defaults/main.yml | Environment variables to specify for the validator node, most importantly `RUST_LOG` | 67 | | `solana_enabled_services` | `[ solana-rpc ]` | List of services to start automatically on boot | 68 | | `solana_disabled_services` | `[ ]` | List of services to set as disabled | 69 | 70 | ### Ports 71 | 72 | The following ports needs to be configured for your RPC server. 73 | 74 | | Name | Default value | Description | 75 | |----------------------|----------------------|----------------------------| 76 | | `solana_gossip_port` | 8001 | Port for gossip traffic (needs to be open publicly in firewall for both TCP and UDP) | 77 | | `solana_rpc_port` | 8899 (+8900) | Ports for incoming RPC (and websocket). This is typically only open on localhost. Place a proxy like `haproxy` in front of these port(s) and don't expose them publicly. | 78 | | `solana_rpc_bind_address` | 127.0.0.1 | Address to bind RPC on. This should typically be localhost. Place a proxy like `haproxy` in front of this to accept public traffic | 79 | | `solana_dynamic_port_range` | 8002-8020 | Port for incoming solana traffic. May need to be open publicly in firewall for UDP. | 80 | 81 | From this list, you can tell that you need at least 8001-8020 open in your firewall for incoming traffic in the default case. 82 | 83 | For pure RPC nodes it may be possible to close down the TPU and TPU forward ports. These ports are dynamically allocated and you can see them by looking at your node in `solana gossip`. If you want to firewall them, you can use this utility: https://github.com/rpcpool/tpu-traffic-classifier. Using this tool you can block incoming TPU and TPU forward on a local node by running: 84 | 85 | `./tpu-traffic-classifier -config-file config.yml -our-localhost -tpu-policy DROP -fwd-policy DROP -update=false` 86 | 87 | Put this in a SystemD service and you can have it start at boot of node and leave it continuously running. 88 | 89 | ### Network specific variables 90 | 91 | Default values for these variables are specified in `vars/{{ solana_network }}-default.yml` (e.g. `vars/mainnet-default.yml`). You can also specify your own by providing the file `{{ solana_network }}.yml`. You will need to specify all these variables unless you rely on the defaults. 92 | 93 | | Name | Default value | Description | 94 | |----------------------|----------------------|----------------------------| 95 | | `solana_network` | mainnet | The solana network this node should join | 96 | | `solana_metrics_config` | see vars/mainnet-default.yml | The metrics endpoint | 97 | | `solana_genesis_hash` | see vars/mainnet-default.yml | The genesis hash for this network | 98 | | `solana_entrypoints` | see vars/mainnet-default.yml | Entrypoint hosts | 99 | | `solana_known_validators` | see vars/mainnet-default.yml | Known validators from where to fetch snapshots and genesis bin on start up | 100 | | `solana_expected_bank_hash` | see vars/mainnet-default.yml | Expected bank hash | 101 | | `solana_expected_shred_version` | see vars/mainnet-default.yml | Expected shred version | 102 | | `solana_index_exclude_keys` | see vars/mainnet-default.yml | Keys to exclude from indexes for performance reasons | 103 | 104 | ### RPC specific variables 105 | 106 | | Name | Default value | Description | 107 | |----------------------|----------------------|----------------------------| 108 | | `solana_rpc_faucet_address` | | Specify an RPC faucet | 109 | | `solana_rpc_history` | true | Whether to provide historical values over RPC | 110 | | `solana_account_index` | program-id spl-token-owner spl-token-mint | Which indexes to enable. These greatly improve performance but slows down start up time and can increase memory requirements. | 111 | 112 | ### Performance variables 113 | 114 | These are variables you can tweak to improve performance 115 | 116 | | Name | Default value | Description | 117 | |----------------------|----------------------|----------------------------| 118 | | `solana_snapshot_compression` | | Whether to compress snapshots or not. Specify none to improve performance. | 119 | | `solana_snapshot_interval_slots` | | How often to take snapshots. Increase to improve performance. Suggested value is 500. | 120 | | `solana_pubsub_max_connections` | 1000 | Maximum number of pubsub connections to allow. | 121 | | `solana_bpf_jit` | | Whether to enable BPF JIT . Default on for devnet. | 122 | | `solana_banking_threads` | 16 | Number of banking threads. | 123 | | `solana_rpc_threads` | | Number of RPC threads (default maximum threads/cores on system) | 124 | | `solana_limit_ledger_size` | `solana default, 250 mio` | Size of the local ledger to store. For a full epoch set a value between 350 mio and 500 mio. For best performance set 50 (minimal value). | 125 | | `solana_accounts_db_caching` | | Whether to enable accounts db caching | 126 | | `solana_accounts_shrink_path` | | You may want to specify another location for the accounts shrinking process | 127 | 128 | ## Bigtable 129 | 130 | You can specify Google Bigtable account credentials for querying blocks not present in local ledger. 131 | 132 | | Name | Default value | Description | 133 | |----------------------|----------------------|----------------------------| 134 | | `solana_bigtable_enabled` | false | Enable bigtable access | 135 | | `solana_bigtable_upload_enabled` | false | Enable bigtable uploading (the credentials you provide below needs write access) | 136 | | `solana_bigtable_project_id` | | Bigtable project id | 137 | | `solana_bigtable_private_key_id` | | Bigtable private key id | 138 | | `solana_bigtable_private_key` | | Bigtable private key | 139 | | `solana_bigtable_client_email` | | Bigtable client email | 140 | | `solana_bigtable_client_id` | | Bigtable client id | 141 | | `solana_bigtable_client_x509_cert_url` | | Bigtable cert url | 142 | 143 | For more information about BigTable see https://github.com/solana-labs/solana-bigtable . 144 | 145 | 146 | ## Handling forks 147 | 148 | Occasionally devnet/testnet will experience forks. In these cases use the following parameters as instructed in Discord: 149 | 150 | | Name | Default value | Description | 151 | |----------------------|----------------------|----------------------------| 152 | | `solana_hard_fork` | | Hard fork | 153 | | `solana_wait_for_supermajority` | | Whether node should wait for supermajority or not | 154 | 155 | ## CPU governor & Sysctl settings 156 | 157 | There are certain configurations that you need to do to get your RPC node running properly. This role can help you make some of these standard config changes. However, full optmisation depends greatly on your hardware so you need to take time to be familiar with how to configure your hardware right. 158 | 159 | However, the most important element of optimisation is the CPU performance governor. This controls boost behaviour and energy usage. On many hosts in DCs they are configured for balance between performance and energy usage. In the case of Solana we really need them to perform at their fastest. To set the servers CPU governor there are three options: 160 | 161 | 1. You have access to BIOS and you set the BIOS cpu setting to `max performance`. This seems to work well for HPE systems. In this case, specify the variable `cpu_governor: bios`. This is sometimes required for AMD EPYC systems too. 162 | 2. You have acccess to BIOS and you set the BIOS cpu setting to `os control`. This should be the typical default. In this case you can leave the `cpu_governor` variable as default or set it explicitly to `cpu_governor: performance`. 163 | 3. You don't have access to BIOS or CPU governor settings. If possible, try to set `cpu_governor: performance`. Otherwise, hopefully your provider has configured it for good performance! 164 | 165 | The second config you need to do is to edit various kernel parameters to fit the Solana RPC use case. 166 | 167 | One option is to deploy `solana-sys-tuner` together with this config to autotune some variables for you. 168 | 169 | A second option, especially if you are new to tuning performance is `tuned` and `tune-adm` from RedHat, where the `throughput-performance` profile is suitable. 170 | 171 | Finally, if you deploy through this role you can also specify a list of sysctl values for this playbook to automatically set up on your host. This allows full control and sets them so that they are permanently configured. 172 | Here is a list of sysctl values that we have used on rpcpool: 173 | 174 | ``` 175 | sysctl_optimisations: 176 | vm.max_map_count: 700000 177 | kernel.nmi_watchdog: 0 178 | # Minimal preemption granularity for CPU-bound tasks: 179 | # (default: 1 msec# (1 + ilog(ncpus)), units: nanoseconds) 180 | kernel.sched_min_granularity_ns: '10000000' 181 | # SCHED_OTHER wake-up granularity. 182 | # (default: 1 msec# (1 + ilog(ncpus)), units: nanoseconds) 183 | kernel.sched_wakeup_granularity_ns: '15000000' 184 | vm.swappiness: '30' 185 | kernel.hung_task_timeout_secs: 600 186 | # this means that virtual memory statistics is gathered less often but is a reasonable trade off for lower latency 187 | vm.stat_interval: 10 188 | vm.dirty_ratio: 40 189 | vm.dirty_background_ratio: 10 190 | vm.dirty_expire_centisecs: 36000 191 | vm.dirty_writeback_centisecs: 3000 192 | vm.dirtytime_expire_seconds: 43200 193 | kernel.timer_migration: 0 194 | # A suggested value for pid_max is 1024 * <# of cpu cores/threads in system> 195 | kernel.pid_max: 65536 196 | net.ipv4.tcp_fastopen: 3 197 | # From solana systuner 198 | # Reference: https://medium.com/@CameronSparr/increase-os-udp-buffers-to-improve-performance-51d167bb1360 199 | net.core.rmem_max: 134217728 200 | net.core.rmem_default: 134217728 201 | net.core.wmem_max: 134217728 202 | net.core.wmem_default: 134217728 203 | ``` 204 | 205 | Example Playbooks 206 | ----------------- 207 | 208 | Mainnet node: 209 | 210 | ``` 211 | - hosts: rpc_nodes 212 | become: true 213 | become_method: sudo 214 | roles: 215 | - { role: rpcpool.solana-rpc, solana_network: mainnet } 216 | ``` 217 | 218 | Testnet node: 219 | 220 | ``` 221 | - hosts: rpc_nodes 222 | become: true 223 | become_method: sudo 224 | roles: 225 | - { role: rpcpool.solana-rpc, solana_network: testnet } 226 | ``` 227 | 228 | Devnet node: 229 | 230 | ``` 231 | - hosts: rpc_nodes 232 | become: true 233 | become_method: sudo 234 | roles: 235 | - { role: rpcpool.solana-rpc, solana_network: devnet } 236 | ``` 237 | 238 | 239 | 240 | Starting the RPC node 241 | -------------------- 242 | 243 | After the deploy you can login to the machine and run `su -l solana` to become the solana user. 244 | 245 | To see the Solana validator command line generated for you during the deploy you can take a look at `/home/solana/bin/solana-rpc.sh`. Remember that any changes to this file will be overwritten next time you run this Ansible. 246 | 247 | For the first start up, you should comment out `--no-genesis-fetch` and `--no-snapshot-fetch` in the file `/home/solana/bin/solana-rpc.sh`. This will allow solana to download the basic files it requires for first time start up. Remember to activate these lines again after you have started the validator for the first time. 248 | 249 | Then start up the solana RPC process by running `systemctl --user start solana-rpc`. You can see status of the process by running `systemctl --user status solana-rpc`. The first start up will take some time. You can monitor start up by running `solana catchup --our-localhost`. 250 | 251 | Finally, to see logs for your Solana RPC node run `journalctl --user -u solana-rpc -f`. 252 | 253 | If this is your first time running a Solana node, you can find more details about how to operate the node on [https://docs.solana.com/running-validator/validator-start](https://docs.solana.com/running-validator/validator-start) and [https://github.com/agjell/sol-tutorials/](https://github.com/agjell/sol-tutorials/). 254 | 255 | 256 | Checking the RPC node 257 | -------------------- 258 | 259 | The basic check after you've veriried that the node has started is to track catchup: 260 | 261 | ``` 262 | solana catchup --our-localhost 263 | ``` 264 | 265 | After this you can continue to check that it is serving RPC calls correctly. 266 | 267 | ## Testing RPC access 268 | You can also try a few easy validation commands (thanks buffalu: https://gist.github.com/buffalu/db6458d4f6a0b70ac303027b61a636af): 269 | 270 | ``` 271 | curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' 272 | {"jsonrpc":"2.0","id":1, "method":"getSlot", "params": [ 273 | { 274 | "commitment": "processed" 275 | } 276 | ]} 277 | ' 278 | 279 | curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' 280 | {"jsonrpc":"2.0","id":1, "method":"getSlot"} 281 | ' 282 | ``` 283 | 284 | ## Testing websocket access 285 | 286 | The easiest way to test websockets is to install the utility `wscat`. To do so you'll need to install NodeJS and NPM and then run `npm install wscat`. 287 | 288 | You can then connect to your websocket in the following way: 289 | 290 | ``` 291 | wscat -c localhost:8900 292 | ``` 293 | 294 | From there you'll get a command prompt where you can manually enter your websocket subscription requests: 295 | 296 | ``` 297 | > {"jsonrpc":"2.0", "id":1, "method":"slotSubscribe"} 298 | ``` 299 | 300 | You should now start receiving regular updates on the slots as they are confirmed by your RPC node. 301 | 302 | RPC node falling behind/not catching up 303 | -------------------- 304 | 305 | The most typical performance issue that an RPC node can face is that it keeps falling behind the network and is not able to catch up. 306 | 307 | If it can't catch up the first time you started it up, this would typically be due to a misconfiguration. The most common issue is your CPU boost frequencies (for more details on CPU config see above): 308 | 309 | * Check that your CPU is recent enough (anything < EPYC 2nd gen on AMD or < Cascade Lake on Intel will struggle) 310 | * Check that your CPU governor is not set to energy saving mode in BIOS and in your kernel settings 311 | * Observe the CPU frequencies when running solana with `watch -n 1 grep MHz /proc/cpuinfo`, you'll need it to be > 3ghz on all cores typically (rule of thumb). You **do not** want to see any core going to 1.4-1.8 ever. 312 | 313 | If it used to be able to catch up but is no longer (or if fixing the CPU didn't solve it): 314 | 315 | * Check memory/cpu/network - do you have good CPU frequencies, are you dipping into swap (not enough memory) or is your provider throttling UDP packets? 316 | + _CPU_: Fix performance governor/boost setting, get newer generation CPU or CPU with better all-cores turbo (check wikichip for details). Remember that MHz is not the same across different generations. Broadwell 3.0 ghz is not the same as Cascade Lake 3.0 ghz or EPYC 3rd gen 3.0 ghz. 317 | + _Network_: Check UDP packet throttling and connectivity. You need at least a 500 mbps pipe without any throttling on UDP. Some providers like to block UDP or throttle it for DDoS protection. This is both on incoming and outgoing. If you are throttled on incoming your node will not receive shreds from the network in time. Check your firewalls that you are not 318 | + _Memory_: Download more RAM. Solana doesn't like to run on swap so if you are regularly dipping into swap you need to fix that. One temporary solution can be to disable `spl-token-owner` / `spl-token-mint` indexes. They have grown really big. 319 | + _Disk_: Check that your NVME for holding ledger and/or accounts isn't dead or dieing. A simple `dmesg` or SMART status query should be able to tell you. 320 | * There's a bug that after heavy getBlocks call over RPC the node stays permanently behind, try a restart of the node and if that helps that may be your issue 321 | * Have you tried unplugging it and plugging it in again? Sometimes it can help to clean your ledger and restart. 322 | * Check your traffic patterns. Certain RPC traffic patterns can **easily** push your node behind. Maybe you need to add another node and split your RPC traffic or you need to ratelimit your calls to problematic queries like `getProgramAccounts`. 323 | 324 | 325 | Access to historical data 326 | -------------------- 327 | 328 | By default, when you start the RPC node it will being building its local ledger from the blocks that it receives over the Solana network. This local ledger starts from the point of the accounts snapshot that you downloaded when your node was starting. If you don't add `--no-snapshot-fetch` to your `solana-validator` command line, the validator will often pull a snapshot from the network when it is starting. This will leave holes or gaps in your ledger between the point where you stopped your RPC node and the point at which it downloaded the accounts snapshot. To avoid this, always specify `--no-snapshot-fetch` after the first time you started the node. Remember that any time you pull a snapshot you will create a hole in the local ledger. 329 | 330 | The size of the local ledger is determined by the parameter `--limit-ledger-size`, which is measured in shreds. A shred is a fixed data unit. The conversion betweens shreds and blocks is not fixed, as blocks can be varying size. Therefore it is very difficult to say how much history measured in time or in number of blocks that your node will store. You will have to tune it according to your needs. A good starting point can be 250-350 million shreds which should cover approximately an epoch, which should in turn mean approximately 3 days. 331 | 332 | The exact amount of data the RPC node will store also depends on the parameters `--enable-cpi-and-log-storage` and `--enable-rpc-transaction-history`. These are necessary for the node to retain and serve full block and transaction data. 333 | 334 | Your node can only provide data which it has stored in its local ledger. This means that your history will always begin from the point at which you started the node (actually: the snapshot slot for which you started the node). If the network is currently at slot N and you pulled a snapshot at slot M, then your node will start to rebuild it's history between slot M and slot N. This is what is happening during `catchup`, the node is processing (replaying) everything that happened between M and N until it catches up with the network and can process all the current incoming data. 335 | 336 | The node can (in theory) store as much history as you can fit on high speed storage (e.g. if you /don't/ specify `--limit-ledger-size` or you give it a huge value). However, this doesn't scale back to genesis. To get all history, you can use the built in Google BigTable support. You can both set your node to upload data to a Google BigTable instance, where it can be permanently available for historical querying. You can also configure your node to support queries to a BigTable instance. In this case, for any queries which the node does not have in its local ledger, it will make a request to Google BigTable and if it finds it in Google BigTable it can pull the data from there. 337 | 338 | Some RPC providers and the Solana Foundation have copies of BigTable that go back to genesis. For more information about this, see https://github.com/solana-labs/solana-bigtable . 339 | 340 | Indexes and performance: or, why is my RPC so slow? 341 | -------------------- 342 | 343 | There are three indexes that the Solana validator generates `program-id`, `spl-token-mint`, `spl-token-owner`. The last two are used to support queries either via `getTokensByOwner`, `getTokenLargestAccounts` or via `getTokensByDelegate`. They are also used to suport queries of `getProgramAccounts` which employ specific filters. 344 | 345 | These indexes have started to grow huge. If you do not need these queries to be fast for your RPC node, then you should remove them as you will reduce memory usage of your node considerably as well as improve start up times. 346 | 347 | If you DO need these RPC calls then you DO need to activate the indexes via the account index flag, otherwise these calls will run intolerably slow. This will require a lot of RAM - generally we don't recommend deploying these with less than 512 gb ram available. 348 | 349 | An alternative to these might be using Geyser plugins, such as the postgres plugin, that can help speed up queries without relying on in-memory indexes: https://github.com/rpcpool/solana-geyser-park. 350 | 351 | 352 | Security concerns 353 | -------------------- 354 | 355 | Security is a big field and you cannot rely on a small guide in a GitHub repo. Typically, at the very least you **should** make sure that your RPC server does not expose port 8899 and 8900 directly without any kind of proxy and access control in front of it. An easy way to do this is to use nginx or HAproxy as a reverse proxy. You can add SSL support and authentication in this way through the built in tools of each of these. 356 | 357 | To be safe, you can ensure that your rpc-bind-address is set to `127.0.0.1` (the default for this role) so that it will only respond to requests locally. 358 | 359 | 360 | 361 | 362 | 363 | 364 | Other playbooks 365 | -------------------- 366 | 367 | Usually you will want to deploy a reverse proxy in front of the Solana RPC. HAproxy is a great option and we have a playbook for configuring HAproxy for a solana rpc server [here](https://github.com/rpcpool/solana-rpc-haproxy-ansible). 368 | 369 | 370 | Other guides and docs 371 | -------------------- 372 | These are some other guides, resources and docs written about Solana RPC: 373 | 374 | - [Solana RPC setup with Traefik](https://github.com/CryptoManufaktur-io/solana-rpc) 375 | - [Solana Accounts DB plugin docs](https://docs.solana.com/developing/plugins/accountsdb_plugin) 376 | - [Solana Accounts DB zoo - list of plugins](https://github.com/rpcpool/solana-accountsdb-zoo) 377 | - [Solana RPC providers](https://solana.com/rpc) 378 | - [Solana AWS validator](https://github.com/solanium-io/aws-solana-validator) 379 | - [Solana RPC gist](https://gist.github.com/buffalu/db6458d4f6a0b70ac303027b61a636af) 380 | - [Solana JSON-RPC caching server by Zubr](https://github.com/zubr-exchange/cacherpc) 381 | - [RPC Cache server by Monadical](https://github.com/Monadical-SAS/rpc-cache-server) 382 | - [Solana RPC proxy](https://github.com/Blue-Terra/solana-rpc-proxy) 383 | - [Autoclock RPC](https://github.com/overclock-validator/autoclock-rpc) 384 | 385 | We make no claims as to the accuracy or quality of any of these docs. Please review and make your own mind for what docs to follow! 386 | 387 | License 388 | ------- 389 | 390 | MIT 391 | 392 | Author Information 393 | ------------------ 394 | 395 | This role was originally developed by [Triton One](https://triton.one). Patches, suggestions and improvements are always welcome. 396 | --------------------------------------------------------------------------------